1d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/** 2d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * $RCSfile$ 3d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * $Revision$ 4d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * $Date$ 5d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 6d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); 7d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * you may not use this file except in compliance with the License. 8d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * You may obtain a copy of the License at 9d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 10d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * http://www.apache.org/licenses/LICENSE-2.0 11d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 12d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Unless required by applicable law or agreed to in writing, software 13d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * distributed under the License is distributed on an "AS IS" BASIS, 14d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * See the License for the specific language governing permissions and 16d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * limitations under the License. 17d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 18d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpackage org.jivesoftware.smack; 19d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 20d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.StreamError; 21d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Random; 22d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/** 23d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Handles the automatic reconnection process. Every time a connection is dropped without 24d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the application explictly closing it, the manager automatically tries to reconnect to 25d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the server.<p> 26d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 27d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The reconnection mechanism will try to reconnect periodically: 28d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <ol> 29d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <li>For the first minute it will attempt to connect once every ten seconds. 30d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <li>For the next five minutes it will attempt to connect once a minute. 31d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <li>If that fails it will indefinitely try to connect once every five minutes. 32d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * </ol> 33d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 34d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @author Francisco Vives 35d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 36d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpublic class ReconnectionManager implements ConnectionListener { 37d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 38d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Holds the connection to the server 39d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private Connection connection; 40d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private Thread reconnectionThread; 41d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private int randomBase = new Random().nextInt(11) + 5; // between 5 and 15 seconds 42d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 43d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Holds the state of the reconnection 44d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen boolean done = false; 45d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 46d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen static { 47d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Create a new PrivacyListManager on every established connection. In the init() 48d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // method of PrivacyListManager, we'll add a listener that will delete the 49d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // instance when the connection is closed. 50d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Connection.addConnectionCreationListener(new ConnectionCreationListener() { 51d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void connectionCreated(Connection connection) { 52d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.addConnectionListener(new ReconnectionManager(connection)); 53d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 54d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }); 55d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 56d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 57d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private ReconnectionManager(Connection connection) { 58d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.connection = connection; 59d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 60d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 61d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 62d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 63d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Returns true if the reconnection mechanism is enabled. 64d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 65d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return true if automatic reconnections are allowed. 66d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 67d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private boolean isReconnectionAllowed() { 68d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return !done && !connection.isConnected() 69d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen && connection.isReconnectionAllowed(); 70d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 71d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 72d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 73d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Starts a reconnection mechanism if it was configured to do that. 74d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The algorithm is been executed when the first connection error is detected. 75d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p/> 76d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The reconnection mechanism will try to reconnect periodically in this way: 77d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <ol> 78d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <li>First it will try 6 times every 10 seconds. 79d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <li>Then it will try 10 times every 1 minute. 80d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <li>Finally it will try indefinitely every 5 minutes. 81d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * </ol> 82d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 83d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen synchronized protected void reconnect() { 84d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (this.isReconnectionAllowed()) { 85d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Since there is no thread running, creates a new one to attempt 86d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // the reconnection. 87d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // avoid to run duplicated reconnectionThread -- fd: 16/09/2010 88d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (reconnectionThread!=null && reconnectionThread.isAlive()) return; 89d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 90d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen reconnectionThread = new Thread() { 91d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 92d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 93d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Holds the current number of reconnection attempts 94d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 95d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private int attempts = 0; 96d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 97d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 98d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Returns the number of seconds until the next reconnection attempt. 99d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 100d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return the number of seconds until the next reconnection attempt. 101d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 102d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private int timeDelay() { 103d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen attempts++; 104d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (attempts > 13) { 105d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return randomBase*6*5; // between 2.5 and 7.5 minutes (~5 minutes) 106d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 107d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (attempts > 7) { 108d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return randomBase*6; // between 30 and 90 seconds (~1 minutes) 109d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 110d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return randomBase; // 10 seconds 111d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 112d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 113d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 114d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The process will try the reconnection until the connection succeed or the user 115d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * cancell it 116d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 117d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void run() { 118d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // The process will try to reconnect until the connection is established or 119d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // the user cancel the reconnection process {@link Connection#disconnect()} 120d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen while (ReconnectionManager.this.isReconnectionAllowed()) { 121d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Find how much time we should wait until the next reconnection 122d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen int remainingSeconds = timeDelay(); 123d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Sleep until we're ready for the next reconnection attempt. Notify 124d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // listeners once per second about how much time remains before the next 125d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // reconnection attempt. 126d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen while (ReconnectionManager.this.isReconnectionAllowed() && 127d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen remainingSeconds > 0) 128d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen { 129d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 130d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Thread.sleep(1000); 131d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen remainingSeconds--; 132d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen ReconnectionManager.this 133d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen .notifyAttemptToReconnectIn(remainingSeconds); 134d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 135d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (InterruptedException e1) { 136d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen e1.printStackTrace(); 137d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Notify the reconnection has failed 138d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen ReconnectionManager.this.notifyReconnectionFailed(e1); 139d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 140d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 141d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 142d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Makes a reconnection attempt 143d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 144d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (ReconnectionManager.this.isReconnectionAllowed()) { 145d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connection.connect(); 146d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 147d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 148d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (XMPPException e) { 149d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Fires the failed reconnection notification 150d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen ReconnectionManager.this.notifyReconnectionFailed(e); 151d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 152d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 153d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 154d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }; 155d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen reconnectionThread.setName("Smack Reconnection Manager"); 156d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen reconnectionThread.setDaemon(true); 157d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen reconnectionThread.start(); 158d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 159d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 160d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 161d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 162d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Fires listeners when a reconnection attempt has failed. 163d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 164d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param exception the exception that occured. 165d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 166d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected void notifyReconnectionFailed(Exception exception) { 167d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (isReconnectionAllowed()) { 168d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen for (ConnectionListener listener : connection.connectionListeners) { 169d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen listener.reconnectionFailed(exception); 170d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 171d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 172d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 173d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 174d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 175d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Fires listeners when The Connection will retry a reconnection. Expressed in seconds. 176d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 177d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param seconds the number of seconds that a reconnection will be attempted in. 178d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 179d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected void notifyAttemptToReconnectIn(int seconds) { 180d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (isReconnectionAllowed()) { 181d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen for (ConnectionListener listener : connection.connectionListeners) { 182d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen listener.reconnectingIn(seconds); 183d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 184d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 185d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 186d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 187d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void connectionClosed() { 188d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen done = true; 189d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 190d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 191d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void connectionClosedOnError(Exception e) { 192d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen done = false; 193d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (e instanceof XMPPException) { 194d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen XMPPException xmppEx = (XMPPException) e; 195d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen StreamError error = xmppEx.getStreamError(); 196d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 197d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // Make sure the error is not null 198d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (error != null) { 199d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen String reason = error.getCode(); 200d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 201d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if ("conflict".equals(reason)) { 202d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return; 203d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 204d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 205d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 206d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 207d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (this.isReconnectionAllowed()) { 208d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.reconnect(); 209d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 210d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 211d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 212d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void reconnectingIn(int seconds) { 213d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // ignore 214d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 215d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 216d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void reconnectionFailed(Exception e) { 217d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // ignore 218d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 219d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 220d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 221d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The connection has successfull gotten connected. 222d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 223d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public void reconnectionSuccessful() { 224d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // ignore 225d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 226d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 227d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen}