/***********************************************************************************
*
* Copyright (c) 2014 Kamil Baczkowicz
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
*
* Kamil Baczkowicz - initial API and implementation and/or initial documentation
*
*/
package pl.baczkowicz.mqttspy.connectivity.reconnection;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pl.baczkowicz.mqttspy.common.generated.ReconnectionSettings;
import pl.baczkowicz.mqttspy.connectivity.BaseMqttConnection;
import pl.baczkowicz.mqttspy.connectivity.MqttConnectionStatus;
import pl.baczkowicz.spy.utils.ThreadingUtils;
import pl.baczkowicz.spy.utils.TimeUtils;
/**
* This class is responsible for (re)establishing connections.
*/
public class ReconnectionManager implements Runnable
{
/** Diagnostic logger. */
private final static Logger logger = LoggerFactory.getLogger(ReconnectionManager.class);
/** Mapping between connections and connectors implemented as runnables. */
private final Map<BaseMqttConnection, Runnable> connections = new HashMap<BaseMqttConnection, Runnable>();
/** Sleep between reconnection cycles. */
private final static int SLEEP = 100;
/** Indicates whether the Reconnection Manager is running. */
private boolean running;
/**
* Adds a connection and its connector to the manager.
*
* @param connection The connection to add
* @param connector The connector runnable
*/
public void addConnection(final BaseMqttConnection connection, final Runnable connector)
{
synchronized (connections)
{
connections.put(connection, connector);
}
}
/**
* Removes a connection from the manager.
*
* @param connection The connection to remove
*/
public void removeConnection(final BaseMqttConnection connection)
{
synchronized (connections)
{
connections.remove(connection);
}
}
/**
* Performs one connecting cycle - checks if any connections need reconnecting.
*/
public void oneCycle()
{
for (final BaseMqttConnection connection : connections.keySet())
{
if (connection.getConnectionStatus().equals(MqttConnectionStatus.CONNECTING))
{
// If already connecting, ignore it
continue;
}
final ReconnectionSettings reconnectionSettings = connection.getMqttConnectionDetails().getReconnectionSettings();
if (connection.getLastConnectionAttemptTimestamp() + reconnectionSettings.getRetryInterval() > TimeUtils.getMonotonicTime())
{
// If we're not due to reconnect yet
continue;
}
if (connection.getConnectionStatus().equals(MqttConnectionStatus.DISCONNECTED)
|| connection.getConnectionStatus().equals(MqttConnectionStatus.NOT_CONNECTED))
{
logger.info("Starting connection {}", connection.getMqttConnectionDetails().getName());
new Thread(connections.get(connection)).start();
}
}
}
/**
* Once called, it runs until 'stop' is called.
*/
public void run()
{
ThreadingUtils.logThreadStarting("Reconnection Manager");
running = true;
while (running)
{
synchronized (connections)
{
oneCycle();
}
if (ThreadingUtils.sleep(SLEEP))
{
break;
}
}
ThreadingUtils.logThreadEnding();
}
/**
* Stops the reconnection manager (graceful shutdown).
*/
public void stop()
{
running = false;
}
}