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}