eu.hgross.blaubot.core.ServerConnectionManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of blaubot Show documentation
Show all versions of blaubot Show documentation
An easy to use publish/subscribe middleware to create and communicate through dynamically created adhoc networks.
package eu.hgross.blaubot.core;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import eu.hgross.blaubot.admin.AbstractAdminMessage;
import eu.hgross.blaubot.admin.CloseRelayConnectionAdminMessage;
import eu.hgross.blaubot.admin.RelayAdminMessage;
import eu.hgross.blaubot.admin.ServerConnectionAvailableAdminMessage;
import eu.hgross.blaubot.admin.ServerConnectionDownAdminMessage;
import eu.hgross.blaubot.core.acceptor.IBlaubotConnectionAcceptor;
import eu.hgross.blaubot.core.acceptor.IBlaubotConnectionListener;
import eu.hgross.blaubot.core.acceptor.IBlaubotIncomingConnectionListener;
import eu.hgross.blaubot.core.connector.IBlaubotConnector;
import eu.hgross.blaubot.messaging.BlaubotChannelManager;
import eu.hgross.blaubot.messaging.BlaubotMessage;
import eu.hgross.blaubot.messaging.BlaubotMessageReceiver;
import eu.hgross.blaubot.messaging.IBlaubotAdminMessageListener;
import eu.hgross.blaubot.messaging.IBlaubotMessageListener;
import eu.hgross.blaubot.mock.BlaubotConnectionQueueMock;
import eu.hgross.blaubot.util.Log;
/**
* Manages the connection to the BlaubotServer (if set).
* It makes use of the BlaubotChannelManager to listen and send admin messages to other devices.
*
* It operates in two modes:
* Client and Master (as the BlaubotChannelManager).
*
* It always sends admin messages if a connection to the server is available or down.
*
* In client mode it listens to RelayAdminMessages and sends them to the available server connection (if any).
*
* In server mode it listens to the Available/Down messages and collects possible connections to the
* server as there could be more than just one.
*/
public class ServerConnectionManager extends LifecycleListenerAdapter {
private static final String LOG_TAG = "ServerConnectionManager";
/**
* The period to check if a connection path to the server exists and needs to be chosen
* (master mode only).
*/
private static final long CONNECTION_SELECT_INTERVAL = 1500;
/**
* Milliseconds to await termination of the connect scheduler
*/
private static final long SHUTDOWN_TERMINATION_TIMEOUT = 2500;
/**
* The main blaubot channel manager
*/
private final BlaubotChannelManager channelManager;
/**
* our own device
*/
private final IBlaubotDevice ownDevice;
/**
* The MAIN blaubot connection manager (the manager used for the actual blaubot network).
* Don't confuse this with the connectionManager used for serverConnector connections.
*/
private final BlaubotConnectionManager mainBlaubotConnectionManager;
/**
* The connection manager holding the connections established from serverConnectors.
* Don't confuse this with the mainBlaubotConnectionManager.
* The connections stored here are simple connections and have to be upgraded to KingdomConnections
* before usage.
*/
private final BlaubotConnectionManager connectionManager;
/**
* The serverConnector injected via the getter. May be null.
*/
private BlaubotServerConnector serverConnector;
/**
* Indicates whether this manager operates in master mode or not
*/
private volatile boolean isMaster = false;
/**
* The facade connection added to the main channel manager if operating as master. May be null.
*/
private volatile IBlaubotConnection facadeConnection;
/**
* The current king device set by the LifecycleListener.
* May be null.
*/
private IBlaubotDevice currentKingDevice;
/**
* Current connection to the server, if in Master mode. May be null.
*/
private BlaubotKingdomConnection currentServerConnection;
/**
* synchronizes the creation and shutdowns of the RelayMessageMediator
*/
private Object serverConnectionLock = new Object();
/**
* Current RelayMessageMediator in client mode. May be null.
* Takes a connection and uses the connection to speak to the server.
* Takes admin relay messages and sends them through the server connection.
* Receives data from the server, wraps them into RelayMessages and sends them
* to the king as admin message.
* On the King side, they will be made available as bytestream to the usual
* channel manager.
*/
private RelayMessageMediator relayMessageMediator;
private Object relayMessageMediatorLock = new Object();
/**
* Schedules the connectionSelectionTask.
* Is created and shut down on setMaster(false/true)
*/
private volatile ScheduledExecutorService connectionSelectionExecutorService;
/**
* A TimerTask that checks if a connection exists and if not tries to connect to the
* server.
*/
private Runnable connectionSelectionTask = new Runnable() {
@Override
public void run() {
try {
if(!isMaster) {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Not master, nothing to do.");
}
return;
}
synchronized (serverConnectionLock) {
final boolean connected = currentServerConnection != null;
if (!connected) {
if (Log.logDebugMessages()) {
// Log.d(LOG_TAG, "Currently no connection to the server available. Selecting ...");
}
List allConnections = connectionManager.getAllConnections();
if (allConnections.isEmpty()) {
if (Log.logDebugMessages()) {
// Log.d(LOG_TAG, "No connection path to the server known ... could not select a connection.");
}
return;
}
// -- we have connections
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "There are " + allConnections.size() + " connections to the server available, selecting one out of these: " + allConnections);
}
// choose and add disconnection handling
/**
* TODO: prioritize a connection from ourselve (king), because it is a direct connection. If we don'T have a direct connection prioritize peasant relay connections over prince relay connections!
*/
IBlaubotConnection chosenConnection = allConnections.get(0);
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Chosen connection: " + chosenConnection);
}
// upgrade to KingdomConnection
final BlaubotKingdomConnection kingdomConnection = BlaubotKingdomConnection.createFromOutboundConnection(chosenConnection, ownDevice.getUniqueDeviceID());
currentServerConnection = kingdomConnection;
kingdomConnection.addConnectionListener(new IBlaubotConnectionListener() {
@Override
public void onConnectionClosed(IBlaubotConnection connection) {
synchronized (serverConnectionLock) {
if (currentServerConnection == kingdomConnection) {
currentServerConnection = null;
}
}
}
});
// add to the main blaubot connection manager, it will bobble up from there to the channel manager
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Adding the kingdom connection to the Blaubot connection manager");
}
mainBlaubotConnectionManager.addConnection(kingdomConnection);
}
}
} catch (Throwable t) {
if (Log.logErrorMessages()) {
Log.e(LOG_TAG, "Task failed", t);
}
t.printStackTrace();
}
}
};
/**
*
* @param channelManager the main blaubot channel manager
* @param ownDevice our own device
* @param mainBlaubotConnectionManager the main blaubot connection manager used by the blaubot instance itself
*/
public ServerConnectionManager(final BlaubotChannelManager channelManager, IBlaubotDevice ownDevice, final BlaubotConnectionManager mainBlaubotConnectionManager) {
this.ownDevice = ownDevice;
this.mainBlaubotConnectionManager = mainBlaubotConnectionManager;
this.connectionManager = new BlaubotConnectionManager(new ArrayList(), new ArrayList());
this.channelManager = channelManager;
this.channelManager.addAdminMessageListener(new IBlaubotAdminMessageListener() {
@Override
public void onAdminMessage(AbstractAdminMessage adminMessage) {
if (adminMessage instanceof ServerConnectionAvailableAdminMessage) {
// create relay connection if master
if(isMaster) {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "A server connection is available through mediator device " + ((ServerConnectionAvailableAdminMessage) adminMessage).getMediatorUniqueDeviceId());
}
final String mediatorUniqueDeviceId = ((ServerConnectionAvailableAdminMessage) adminMessage).getMediatorUniqueDeviceId();
final String recipientUniqueDeviceId = ((ServerConnectionAvailableAdminMessage) adminMessage).getRecipientUniqueDeviceId();
final BlaubotServerRelayConnection conn = new BlaubotServerRelayConnection(mediatorUniqueDeviceId, recipientUniqueDeviceId);
// remember the connection
onConnectionAvailable(conn);
}
} else if (adminMessage instanceof ServerConnectionDownAdminMessage) {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "A server connection is NOT available anymore through mediator device " + ((ServerConnectionDownAdminMessage) adminMessage).getMediatorUniqueDeviceId());
}
// only interesting for the master
// the master is only interested in this information, if he is using the connection actively or if he wants to know, which devices have a connection to the server
// all this is handled through the usual blaubot connection manager (this.connectionManager) and the wrapping connection
// BlaubotServerRelayConnection, which is also listening to this admin message to trigger te onDisconnect listeners.
// so nothing to do here
} else if (adminMessage instanceof RelayAdminMessage || adminMessage instanceof CloseRelayConnectionAdminMessage) {
if(!isMaster) {
/**
* If we are not master, we have the mediator role.
* important here:
* Do we already have a mediator?
* if yes -> do nothing, the mediator should do the rest
* if no -> Get a serverconnection from the ConnectionManager. There has to be a connection because RelayAdminMessages and CloseRelayConnectionAdminMessage
* are only send after we sent ServerConnectionAvailable but of course there could be some timing problems and the connection is not there.
* Then create the mediator.
*/
synchronized (relayMessageMediatorLock) {
if(relayMessageMediator == null) {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Got a RelayAdminMessage and had no relayMessageMediator. Creating one.");
}
List allServerConnections = ServerConnectionManager.this.connectionManager.getAllConnections();
if(!allServerConnections.isEmpty()) {
IBlaubotConnection connection = allServerConnections.get(0);
if(connection instanceof BlaubotServerRelayConnection) {
// TODO: concurreny problem here on change to master mode (got a websocket connection here)
// TODO sync isMaster flag and setMaster()
throw new RuntimeException(""+allServerConnections);
}
final RelayMessageMediator mediator = new RelayMessageMediator(connection);
// maintain reference
connection.addConnectionListener(new IBlaubotConnectionListener() {
@Override
public void onConnectionClosed(IBlaubotConnection connection) {
if(relayMessageMediator == mediator) {
relayMessageMediator = null;
}
}
});
relayMessageMediator = mediator;
mediator.activate();
// kick the current admin message in
mediator.onAdminMessage(adminMessage);
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Created a RelayMessageMediator for connection " + connection);
}
} else {
if (Log.logErrorMessages()) {
Log.e(LOG_TAG, "No mediator to resend the received RelayAdminMessage.");
}
}
}
}
}
}
}
});
}
/**
* Handles everything if we get aware of a available connection
* @param connection the newly available connection
*/
private void onConnectionAvailable(IBlaubotConnection connection) {
if(!isMaster) {
// send up message to king, if we are not the king
ServerConnectionAvailableAdminMessage availableAdminMessage = new ServerConnectionAvailableAdminMessage(ownDevice.getUniqueDeviceID(), connection.getRemoteDevice().getUniqueDeviceID());
channelManager.publishToAllConnections(availableAdminMessage.toBlaubotMessage());
connectionManager.addConnection(connection);
} else {
// if king, we store the connection in the connection manager, where it will be automatically removed, if not available anymore.
connectionManager.addConnection(connection);
}
}
/**
* sets the server connector
*
* @param serverConnector the connector
*/
public synchronized void setServerConnector(BlaubotServerConnector serverConnector) {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "ServerConnector was injected: " + serverConnector);
}
if (this.serverConnector != null) {
// -- there is already a serverConnector
// shut it down first
BlaubotServerConnector curServerConnector = this.serverConnector;
this.serverConnector = null;
curServerConnector.setIncomingConnectionListener(null);
curServerConnector.deactivateServerConnector();
final IBlaubotConnection serverConnection = curServerConnector.getServerConnection();
if (serverConnection != null) {
serverConnection.disconnect();
}
}
this.serverConnector = serverConnector;
this.serverConnector.setIncomingConnectionListener(new IBlaubotIncomingConnectionListener() {
@Override
public void onConnectionEstablished(IBlaubotConnection connection) {
// add the listener to remove everything if the connections is disconnected
connection.addConnectionListener(new IBlaubotConnectionListener() {
@Override
public void onConnectionClosed(IBlaubotConnection connection) {
if(!isMaster) {
// send down message to king, if we are not king/master to let him handle any dependencies on this connection (through RelayConnection for example)
ServerConnectionDownAdminMessage downAdminMessage = new ServerConnectionDownAdminMessage(ownDevice.getUniqueDeviceID());
channelManager.publishToAllConnections(downAdminMessage.toBlaubotMessage());
}
}
});
// add the connection to the connectionManager (only the server connections)
onConnectionAvailable(connection);
}
});
if (isConnected) {
this.serverConnector.activateServerConnector();
}
}
/**
* @return the serverConnector or null, if never set by the user
*/
public BlaubotServerConnector getServerConnector() {
return serverConnector;
}
/**
* Indicates, whether we are connected to a blaubot network or not.
* Mainly used to activate a ServerConnector, that is attached after blaubot was already
* started.
*/
private volatile boolean isConnected = false;
@Override
public void onConnected() {
isConnected = true;
if (this.serverConnector != null) {
this.serverConnector.activateServerConnector();
}
}
@Override
public void onDisconnected() {
isConnected = false;
if (this.serverConnector != null) {
this.serverConnector.deactivateServerConnector();
}
clear(); // disconnects the connection, if any
currentKingDevice = null;
}
@Override
public void onDeviceLeft(IBlaubotDevice blaubotDevice) {
if(isMaster) {
// send an on connection down for this device to myself to be sure, that it is handled properly
ServerConnectionDownAdminMessage downAdminMessage = new ServerConnectionDownAdminMessage(blaubotDevice.getUniqueDeviceID());
channelManager.publishToSingleDevice(downAdminMessage.toBlaubotMessage(), ownDevice.getUniqueDeviceID());
}
}
@Override
public void onKingDeviceChanged(IBlaubotDevice oldKing, IBlaubotDevice newKing) {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "King device changed. Clearing state and disconnecting all connections.");
}
clear();
this.currentKingDevice = newKing;
}
/**
* clears the state and disconnects all server connections
*/
private void clear() {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Clearing state: Close connections");
}
for(IBlaubotConnection conn : this.connectionManager.getAllConnections()) {
conn.disconnect();
}
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Clearing state: RelayMessageMediator");
}
synchronized (relayMessageMediatorLock) {
if(this.relayMessageMediator != null) {
relayMessageMediator.serverConnection.disconnect();
this.relayMessageMediator.deactivate();
this.relayMessageMediator = null;
}
}
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Clearing state: ServerConnection");
}
synchronized (serverConnectionLock) {
if(this.currentServerConnection != null) {
this.currentServerConnection.disconnect();
this.currentServerConnection = null;
}
}
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "State cleared.");
}
}
/**
* Sets whether this manager operates in master mode or not
* @param isMaster true iff operating as master
*/
public void setMaster(final boolean isMaster) {
// TODO possible concurrency problems here. Maybe use a single threaded queue to synchronize this things
final boolean changed = isMaster != this.isMaster;
if(changed) {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Changed operating mode to " + (isMaster ? "master":"client"));
}
clear();
if(isMaster) {
// if there was a mediator from a former peasant/prince state, shut it down
synchronized (relayMessageMediatorLock) {
if(relayMessageMediator != null) {
relayMessageMediator.serverConnection.disconnect();
relayMessageMediator.deactivate();
relayMessageMediator = null;
}
}
// activate scheduler
if (this.connectionSelectionExecutorService != null) {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Executer already started ...");
}
return; // already started
}
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Starting up the server connection selection task scheduler.");
}
this.connectionSelectionExecutorService = Executors.newSingleThreadScheduledExecutor();
this.connectionSelectionExecutorService.scheduleAtFixedRate(connectionSelectionTask, 0, CONNECTION_SELECT_INTERVAL, TimeUnit.MILLISECONDS);
} else {
// deactivate scheduler
if (this.connectionSelectionExecutorService != null) {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Shutting down the executor service ...");
}
this.connectionSelectionExecutorService.shutdownNow();
try {
final boolean timedOut = !this.connectionSelectionExecutorService.awaitTermination(SHUTDOWN_TERMINATION_TIMEOUT, TimeUnit.MILLISECONDS);
if (timedOut && Log.logErrorMessages()) {
Log.e(LOG_TAG, "ExecutorService termination timeout");
}
} catch (InterruptedException e) {
} finally {
this.connectionSelectionExecutorService = null;
}
}
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Disconnecting all server connections.");
}
// disconnect all connections
for(IBlaubotConnection conn : this.connectionManager.getAllConnections()) {
conn.disconnect();
}
}
}
this.isMaster = isMaster;
}
/**
* @return the currently USED server connection by the channel manager or null, if no connection exists
*/
public BlaubotKingdomConnection getCurrentlyUsedServerConnection() {
synchronized (serverConnectionLock) {
return currentServerConnection;
}
}
/**
*
* @return list of blaubotconnections the server collected by the manager
*/
public List getConnectionsToServer() {
return new ArrayList<>(connectionManager.getAllConnections());
}
/**
* The mediator that actually sends the relay messages if not in master mode.
* Is created for one kingdom connection.
* Note that it registers itself to the channel manager in the constructor and therefore misses all
* previously received admin messages. The initial admin message has therefore to be injected
* via onAdminMessage().
*
* It manages the real connection to the server (the direct connection).
* To do that, it uses a MessageSender and MessageReceiver pair (for chunking).
*/
private class RelayMessageMediator implements IBlaubotAdminMessageListener {
private static final String LOG_TAG = "RelayMessageMediator";
private final IBlaubotConnection serverConnection;
/**
* receives messages from the server
*/
private final BlaubotMessageReceiver messageReceiver;
/**
* @param serverConnection the serverConnection to be relayed
*/
public RelayMessageMediator(final IBlaubotConnection serverConnection) {
this.serverConnection = serverConnection;
this.messageReceiver = new BlaubotMessageReceiver(serverConnection);
this.messageReceiver.setForwardChunks(true); // forward chunked messages (don't inspect them)
// handles broken connections
serverConnection.addConnectionListener(new IBlaubotConnectionListener() {
@Override
public void onConnectionClosed(IBlaubotConnection connection) {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "Server connection closed. Deactivating messageReceiver.");
}
messageReceiver.deactivate(null);
channelManager.removeAdminMessageListener(RelayMessageMediator.this);
}
});
// listen to messages from the server
this.messageReceiver.addMessageListener(new IBlaubotMessageListener() {
@Override
public void onMessage(BlaubotMessage blaubotMessage) {
// if (Log.logDebugMessages()) {
// Log.d(LOG_TAG, "Got a message from the server connection, dispatching it via RelayMessage");
// }
// got a message from the server, relay to the king
RelayAdminMessage relayAdminMessage = new RelayAdminMessage(blaubotMessage.toBytes());
channelManager.publishToAllConnections(relayAdminMessage.toBlaubotMessage());
}
});
// listens to messages from the king
channelManager.addAdminMessageListener(this);
}
public void activate() {
messageReceiver.activate();
}
public void deactivate() {
messageReceiver.deactivate(null);
}
@Override
public void onAdminMessage(AbstractAdminMessage adminMessage) {
if (adminMessage instanceof RelayAdminMessage) {
if(Log.logDebugMessages()) {
BlaubotMessage unwrappedMessage = ((RelayAdminMessage) adminMessage).getAsBlaubotMessage();
//if (!unwrappedMessage.getMessageType().isKeepAliveMessage()) {
// Log.d(LOG_TAG, "Dispatching a relay message to the server connection: " + unwrappedMessage);
//}
}
byte[] messageBytes = ((RelayAdminMessage) adminMessage).getMessageBytes();
try {
serverConnection.write(messageBytes);
} catch (IOException e) {
// handled by the connection manager
}
} else if(adminMessage instanceof CloseRelayConnectionAdminMessage) {
String mediatorUniqueDeviceId = ((CloseRelayConnectionAdminMessage) adminMessage).getMediatorUniqueDeviceId();
if (mediatorUniqueDeviceId.equals(ownDevice.getUniqueDeviceID())) {
if(Log.logDebugMessages()) {
Log.d(LOG_TAG, "Got instructions to close the relay connection. Disconnecting direct connection to the server ...");
}
serverConnection.disconnect();
// will notify listeners and finally send an down admin message to the king ...
}
}
}
}
/**
* A relay connection is a connection that uses admin messages to transport bytes from
* A to C using an intermediate participant B (mediator).
* There are connections A to B and B to C and the relay connection uses an established
* network to make the transitive connection between A and C (recipient).
*
* The BlaubotServerRelayConnection is only created on A (the king).
* It needs the RelayMessageMediator instance running on B.
* B needs to have a IBlaubotConnection to C.
*
* Note that it only seems like the connection is capable of sending arbitrary bytes through
* it but since we are using MessageReceivers internally it is only capable of sending and
* receiving full blaubot message byte packages!
*/
public class BlaubotServerRelayConnection extends BlaubotConnectionQueueMock implements IBlaubotAdminMessageListener {
private UUID uuid = UUID.randomUUID();
private static final String LOG_TAG = "BlaubotServerRelayConnection";
/**
* The unique device id of the device over which the relay messages are relayed
*/
private final String mediatorUniqueDeviceId;
/**
* The final destination where data is sent to on write(*).. calls.
*/
private final String recipientUniqueDeviceId;
/**
* Everything thaht is written to this connection via it's write(*) methods will be received
* by this receiver locally and then be wrapped into a RelayMessage to be send as admin
* message via the ChannelManager.
*/
private final BlaubotMessageReceiver messageReceiver;
/**
* Note: starts a thread!
* @param mediatorUniqueDeviceId the uniqueDeviceId to which the data should be send (via relay message) when using the write(*) methods.
* @param recipientUniqueDeviceId the recipient's unique device id (Server/King)
*/
public BlaubotServerRelayConnection(final String mediatorUniqueDeviceId, String recipientUniqueDeviceId) {
super(new BlaubotDevice(recipientUniqueDeviceId));
/*
* This connection can read the things written via write() to this connection instance.
*/
final BlaubotConnectionQueueMock dummyConnection = getOtherEndpointConnection(new BlaubotDevice("Internal BlaubotServerRelayConnection DummyDevice "));
this.messageReceiver = new BlaubotMessageReceiver(dummyConnection);
this.messageReceiver.setForwardChunks(true); // we want to just forward junks (not collect them as whole to be chunked again later)
this.messageReceiver.activate();
this.messageReceiver.addMessageListener(new IBlaubotMessageListener() {
@Override
public void onMessage(BlaubotMessage blaubotMessage) {
// wrap the message into a relay admin message
final RelayAdminMessage relayAdminMessage = new RelayAdminMessage(blaubotMessage.toBytes());
// create a blaubot mesasge from the relay admin message (= blaubot message containing which is a relay admin message, which contains a blaubotmessage ;-))
final BlaubotMessage msg = relayAdminMessage.toBlaubotMessage();
// if (!blaubotMessage.getMessageType().isKeepAliveMessage()) {
// Log.d(LOG_TAG, "Dispatching ... Sending RelayAdminMessage to mediator: " + msg);
// }
// send it to the mediator
channelManager.publishToSingleDevice(msg, mediatorUniqueDeviceId);
}
});
this.recipientUniqueDeviceId = recipientUniqueDeviceId;
this.mediatorUniqueDeviceId = mediatorUniqueDeviceId;
// listener handling (cleanup and wiring)
channelManager.addAdminMessageListener(this);
this.addConnectionListener(new IBlaubotConnectionListener() {
@Override
public void onConnectionClosed(IBlaubotConnection connection) {
channelManager.removeAdminMessageListener(BlaubotServerRelayConnection.this);
}
});
addConnectionListener(new IBlaubotConnectionListener() {
@Override
public void onConnectionClosed(IBlaubotConnection connection) {
messageReceiver.deactivate(null);
}
});
}
@Override
public void onAdminMessage(AbstractAdminMessage adminMessage) {
if (adminMessage instanceof ServerConnectionDownAdminMessage) {
// this block only happens on the master
if(((ServerConnectionDownAdminMessage) adminMessage).getMediatorUniqueDeviceId().equals(mediatorUniqueDeviceId)) {
if(Log.logDebugMessages()) {
Log.d(LOG_TAG, "A relay connection is down (" + mediatorUniqueDeviceId + ") ");
}
// if the mediator has no active connection to the server anymore, we disconnect the connection.
// note that this message will also be received if an onDeviceLeft() for this device occurs.
_disconnect(); // triggers its own listeners
}
} else if (adminMessage instanceof RelayAdminMessage) {
// if(Log.logDebugMessages()) {
// Log.d(LOG_TAG, "Dispatching relay admin message of " + ((RelayAdminMessage) adminMessage).getMessageBytes().length + " bytes to input stream of the relay connection");
// }
// put data to the queue. This bytes can then be read via the read(*) methods.
byte[] messageBytes = ((RelayAdminMessage) adminMessage).getMessageBytes();
writeMockDataToInputStream(messageBytes);
}
}
private volatile boolean notifiedDisconnect = false;
@Override
protected void notifyDisconnected() {
if (notifiedDisconnect) {
return;
}
super.notifyDisconnected();
notifiedDisconnect = true;
}
@Override
public void disconnect() {
if (Log.logDebugMessages()) {
Log.d(LOG_TAG, "disconnect() called, sending CloseRelayConnectionAdminMessage");
}
// send disconnect instruction to mediator
CloseRelayConnectionAdminMessage adminMessage = new CloseRelayConnectionAdminMessage(mediatorUniqueDeviceId);
final boolean sent = channelManager.publishToSingleDevice(adminMessage.toBlaubotMessage(), mediatorUniqueDeviceId);
if (!sent) {
if (Log.logWarningMessages()) {
Log.w(LOG_TAG, "CloseRelayConnectionAdminMessage could not be send on disconnect(). Assuming connectivity broken and notifying listener that this connection is disconnected.");
}
// if we are not able to send this instruction, disconnect locally
_disconnect();
}
// the actual disconnect will be announced by an admin message (either on device left or a direct down message)
// and then call _disconnect();
}
private void _disconnect() {
super.disconnect();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
BlaubotServerRelayConnection that = (BlaubotServerRelayConnection) o;
if (uuid != null ? !uuid.equals(that.uuid) : that.uuid != null) return false;
return true;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (uuid != null ? uuid.hashCode() : 0);
return result;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("BlaubotServerRelayConnection{");
sb.append("mediatorUniqueDeviceId='").append(mediatorUniqueDeviceId).append('\'');
sb.append(", recipientUniqueDeviceId='").append(recipientUniqueDeviceId).append('\'');
sb.append('}');
return sb.toString();
}
public String getMediatorUniqueDeviceId() {
return mediatorUniqueDeviceId;
}
public String getRecipientUniqueDeviceId() {
return recipientUniqueDeviceId;
}
}
}