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