From 941f29e92842f6f787bf8d504d5a8eab4fcc3105 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 29 Sep 2017 15:19:10 +0200 Subject: [PATCH] Improve ReconnectionManager Fixes SMACK-778. --- .../smack/ReconnectionManager.java | 70 +++++++++++++------ 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java b/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java index c2b35110d..2cd0b7c8d 100644 --- a/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java +++ b/smack-core/src/main/java/org/jivesoftware/smack/ReconnectionManager.java @@ -43,7 +43,10 @@ import org.jivesoftware.smack.util.Async; * * * {@link ReconnectionPolicy#FIXED_DELAY} - The reconnection mechanism will try to reconnect after a fixed delay - * independently from the number of reconnection attempts already performed + * independently from the number of reconnection attempts already performed. + *

+ * Interrupting the reconnection thread will abort the reconnection mechanism. + *

* * @author Francisco Vives * @author Luca Stucchi @@ -163,7 +166,7 @@ public final class ReconnectionManager { private ReconnectionManager(AbstractXMPPConnection connection) { weakRefConnection = new WeakReference(connection); - reconnectionRunnable = new Thread() { + reconnectionRunnable = new Runnable() { /** * Holds the current number of reconnection attempts @@ -211,6 +214,10 @@ public final class ReconnectionManager { if (connection == null) { return; } + + // Reset attempts to zero since a new reconnection cycle is started once this runs. + attempts = 0; + // The process will try to reconnect until the connection is established or // the user cancel the reconnection process AbstractXMPPConnection.disconnect(). while (isReconnectionPossible(connection)) { @@ -219,7 +226,10 @@ public final class ReconnectionManager { // Sleep until we're ready for the next reconnection attempt. Notify // listeners once per second about how much time remains before the next // reconnection attempt. - while (isReconnectionPossible(connection) && remainingSeconds > 0) { + while (remainingSeconds > 0) { + if (!isReconnectionPossible(connection)) { + return; + } try { Thread.sleep(1000); remainingSeconds--; @@ -228,8 +238,9 @@ public final class ReconnectionManager { } } catch (InterruptedException e) { - LOGGER.log(Level.FINE, "waiting for reconnection interrupted", e); - break; + LOGGER.log(Level.FINE, "Reconnection Thread was interrupted, aborting reconnection mechanism", e); + // Exit the reconnection thread in case it was interrupted. + return; } } @@ -237,24 +248,18 @@ public final class ReconnectionManager { listener.reconnectingIn(0); } + if (!isReconnectionPossible(connection)) { + return; + } // Makes a reconnection attempt try { - if (isReconnectionPossible(connection)) { - try { - connection.connect(); - } catch (SmackException.AlreadyConnectedException e) { - LOGGER.log(Level.FINER, "Connection was already connected on reconnection attempt", e); - } + try { + connection.connect(); } - // TODO Starting with Smack 4.2, connect() will no - // longer login automatically. So change this and the - // previous lines to connection.connect().login() in the - // 4.2, or any later, branch. - if (!connection.isAuthenticated()) { - connection.login(); + catch (SmackException.AlreadyConnectedException e) { + LOGGER.log(Level.FINER, "Connection was already connected on reconnection attempt", e); } - // Successfully reconnected. - attempts = 0; + connection.login(); } catch (SmackException.AlreadyLoggedInException e) { // This can happen if another thread concurrently triggers a reconnection @@ -262,12 +267,21 @@ public final class ReconnectionManager { // failure. See also SMACK-725. LOGGER.log(Level.FINER, "Reconnection not required, was already logged in", e); } - catch (SmackException | IOException | XMPPException | InterruptedException e) { + catch (SmackException | IOException | XMPPException e) { // Fires the failed reconnection notification for (ConnectionListener listener : connection.connectionListeners) { listener.reconnectionFailed(e); } + // Failed to reconnect, try again. + continue; + } catch (InterruptedException e) { + LOGGER.log(Level.FINE, "Reconnection Thread was interrupted, aborting reconnection mechanism", e); + // Exit the reconnection thread in case it was interrupted. + return; } + + // Successfully reconnected . + return; } } }; @@ -314,7 +328,7 @@ public final class ReconnectionManager { * * @return true, if the reconnection mechanism is enabled. */ - public boolean isAutomaticReconnectEnabled() { + public synchronized boolean isAutomaticReconnectEnabled() { return automaticReconnectEnabled; } @@ -348,6 +362,20 @@ public final class ReconnectionManager { "Smack Reconnection Manager (" + connection.getConnectionCounter() + ')'); } + /** + * Abort a possibly running reconnection mechanism. + * + * @since 4.2.2 + */ + public synchronized void abortPossiblyRunningReconnection() { + if (reconnectionThread == null) { + return; + } + + reconnectionThread.interrupt(); + reconnectionThread = null; + } + private final ConnectionListener connectionListener = new AbstractConnectionListener() { @Override