mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-12-24 13:27:59 +01:00
226 lines
No EOL
8.6 KiB
Java
226 lines
No EOL
8.6 KiB
Java
/**
|
|
*
|
|
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package org.jivesoftware.smack;
|
|
|
|
import org.jivesoftware.smack.packet.StreamError;
|
|
import java.util.Random;
|
|
import java.util.logging.Logger;
|
|
/**
|
|
* Handles the automatic reconnection process. Every time a connection is dropped without
|
|
* the application explictly closing it, the manager automatically tries to reconnect to
|
|
* the server.<p>
|
|
*
|
|
* The reconnection mechanism will try to reconnect periodically:
|
|
* <ol>
|
|
* <li>For the first minute it will attempt to connect once every ten seconds.
|
|
* <li>For the next five minutes it will attempt to connect once a minute.
|
|
* <li>If that fails it will indefinitely try to connect once every five minutes.
|
|
* </ol>
|
|
*
|
|
* @author Francisco Vives
|
|
*/
|
|
public class ReconnectionManager implements ConnectionListener {
|
|
private static Logger log = Logger.getLogger(ReconnectionManager.class.getName());
|
|
|
|
// Holds the connection to the server
|
|
private Connection connection;
|
|
private Thread reconnectionThread;
|
|
private int randomBase = new Random().nextInt(11) + 5; // between 5 and 15 seconds
|
|
|
|
// Holds the state of the reconnection
|
|
boolean done = false;
|
|
|
|
static {
|
|
// Create a new PrivacyListManager on every established connection. In the init()
|
|
// method of PrivacyListManager, we'll add a listener that will delete the
|
|
// instance when the connection is closed.
|
|
Connection.addConnectionCreationListener(new ConnectionCreationListener() {
|
|
public void connectionCreated(Connection connection) {
|
|
connection.addConnectionListener(new ReconnectionManager(connection));
|
|
}
|
|
});
|
|
}
|
|
|
|
private ReconnectionManager(Connection connection) {
|
|
this.connection = connection;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns true if the reconnection mechanism is enabled.
|
|
*
|
|
* @return true if automatic reconnections are allowed.
|
|
*/
|
|
private boolean isReconnectionAllowed() {
|
|
return !done && !connection.isConnected()
|
|
&& connection.isReconnectionAllowed();
|
|
}
|
|
|
|
/**
|
|
* Starts a reconnection mechanism if it was configured to do that.
|
|
* The algorithm is been executed when the first connection error is detected.
|
|
* <p/>
|
|
* The reconnection mechanism will try to reconnect periodically in this way:
|
|
* <ol>
|
|
* <li>First it will try 6 times every 10 seconds.
|
|
* <li>Then it will try 10 times every 1 minute.
|
|
* <li>Finally it will try indefinitely every 5 minutes.
|
|
* </ol>
|
|
*/
|
|
synchronized protected void reconnect() {
|
|
if (this.isReconnectionAllowed()) {
|
|
// Since there is no thread running, creates a new one to attempt
|
|
// the reconnection.
|
|
// avoid to run duplicated reconnectionThread -- fd: 16/09/2010
|
|
if (reconnectionThread!=null && reconnectionThread.isAlive()) return;
|
|
|
|
reconnectionThread = new Thread() {
|
|
|
|
/**
|
|
* Holds the current number of reconnection attempts
|
|
*/
|
|
private int attempts = 0;
|
|
|
|
/**
|
|
* Returns the number of seconds until the next reconnection attempt.
|
|
*
|
|
* @return the number of seconds until the next reconnection attempt.
|
|
*/
|
|
private int timeDelay() {
|
|
attempts++;
|
|
if (attempts > 13) {
|
|
return randomBase*6*5; // between 2.5 and 7.5 minutes (~5 minutes)
|
|
}
|
|
if (attempts > 7) {
|
|
return randomBase*6; // between 30 and 90 seconds (~1 minutes)
|
|
}
|
|
return randomBase; // 10 seconds
|
|
}
|
|
|
|
/**
|
|
* The process will try the reconnection until the connection succeed or the user
|
|
* cancell it
|
|
*/
|
|
public void run() {
|
|
// The process will try to reconnect until the connection is established or
|
|
// the user cancel the reconnection process {@link Connection#disconnect()}
|
|
while (ReconnectionManager.this.isReconnectionAllowed()) {
|
|
// Find how much time we should wait until the next reconnection
|
|
int remainingSeconds = timeDelay();
|
|
// 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 (ReconnectionManager.this.isReconnectionAllowed() &&
|
|
remainingSeconds > 0)
|
|
{
|
|
try {
|
|
Thread.sleep(1000);
|
|
remainingSeconds--;
|
|
ReconnectionManager.this
|
|
.notifyAttemptToReconnectIn(remainingSeconds);
|
|
}
|
|
catch (InterruptedException e1) {
|
|
log.warning("Sleeping thread interrupted");
|
|
// Notify the reconnection has failed
|
|
ReconnectionManager.this.notifyReconnectionFailed(e1);
|
|
}
|
|
}
|
|
|
|
// Makes a reconnection attempt
|
|
try {
|
|
if (ReconnectionManager.this.isReconnectionAllowed()) {
|
|
connection.connect();
|
|
}
|
|
}
|
|
catch (XMPPException e) {
|
|
// Fires the failed reconnection notification
|
|
ReconnectionManager.this.notifyReconnectionFailed(e);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
reconnectionThread.setName("Smack Reconnection Manager");
|
|
reconnectionThread.setDaemon(true);
|
|
reconnectionThread.start();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fires listeners when a reconnection attempt has failed.
|
|
*
|
|
* @param exception the exception that occured.
|
|
*/
|
|
protected void notifyReconnectionFailed(Exception exception) {
|
|
if (isReconnectionAllowed()) {
|
|
for (ConnectionListener listener : connection.connectionListeners) {
|
|
listener.reconnectionFailed(exception);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fires listeners when The Connection will retry a reconnection. Expressed in seconds.
|
|
*
|
|
* @param seconds the number of seconds that a reconnection will be attempted in.
|
|
*/
|
|
protected void notifyAttemptToReconnectIn(int seconds) {
|
|
if (isReconnectionAllowed()) {
|
|
for (ConnectionListener listener : connection.connectionListeners) {
|
|
listener.reconnectingIn(seconds);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void connectionClosed() {
|
|
done = true;
|
|
}
|
|
|
|
public void connectionClosedOnError(Exception e) {
|
|
done = false;
|
|
if (e instanceof XMPPException) {
|
|
XMPPException xmppEx = (XMPPException) e;
|
|
StreamError error = xmppEx.getStreamError();
|
|
|
|
// Make sure the error is not null
|
|
if (error != null) {
|
|
String reason = error.getCode();
|
|
|
|
if ("conflict".equals(reason)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.isReconnectionAllowed()) {
|
|
this.reconnect();
|
|
}
|
|
}
|
|
|
|
public void reconnectingIn(int seconds) {
|
|
// ignore
|
|
}
|
|
|
|
public void reconnectionFailed(Exception e) {
|
|
// ignore
|
|
}
|
|
|
|
/**
|
|
* The connection has successfull gotten connected.
|
|
*/
|
|
public void reconnectionSuccessful() {
|
|
// ignore
|
|
}
|
|
|
|
} |