package NetworkAdapter.Implementation;
import EnvironmentPluginAPI.CustomNetworkMessages.NetworkMessage;
import NetworkAdapter.Interface.Exceptions.ConnectionLostException;
import NetworkAdapter.Interface.Exceptions.NotConnectedException;
import NetworkAdapter.Messages.ConnectionEndMessage;
import java.net.InetAddress;
/**
* This class represents a channel connection. It runs in an own thread and allows the sending and event based receiving
* of network messages.
*/
class NetworkChannel<M extends NetworkMessage> extends Thread {
private NetworkAccessProtocol networkAccessProtocol;
private NetworkMessageReceivedHandle handle;
private ClassLoader parentThreadClassLoader;
private boolean disconnecting = false;
/**
* Creates a thread that receives and sends messages on the given network protocol.
*
* @param networkAccessProtocol an established protocol != null
* @param handle the handle for the event of an incoming network message
*/
public NetworkChannel(NetworkAccessProtocol networkAccessProtocol, NetworkMessageReceivedHandle handle) {
super("NetworkChannel");
this.networkAccessProtocol = networkAccessProtocol;
this.handle = handle;
}
@Override
public void run() {
while (!isInterrupted()) {
try {
NetworkMessage message = networkAccessProtocol.readMessage();
if (message instanceof ConnectionEndMessage) {
forceClose();
disconnecting = true;
} else {
deliverMessage(message);
}
} catch (ConnectionLostException e) {
if (!isInterrupted()) {
deliverException(e);
}
interrupt();
} catch (NotConnectedException e) {
deliverException(e);
interrupt();
}
}
}
/**
* Encodes the network message and tries to send it via the socket.
*
* @param message the message to send via the network
* @throws ConnectionLostException if the connection to the remote end was not reachable.
*/
public void sendNetworkMessage(NetworkMessage message) throws ConnectionLostException {
networkAccessProtocol.writeMessage(message);
}
/**
* calls listener, if present, and delivers message
*
* @param message the NetworkMessage that is to distribute != null
*/
protected void deliverMessage(NetworkMessage message) throws NotConnectedException, ConnectionLostException {
if (handle != null) {
handle.handleMessage(message);
}
}
protected void deliverException(Exception exception) {
if (handle != null) {
handle.handleException(exception);
}
}
/**
* The remote end's IP address
*
* @return != null
*/
public InetAddress getRemoteAddress() {
return networkAccessProtocol.getClientAddress();
}
/**
* sends the connectionEndMessage and closes the connection.
*
* @param message
* @throws ConnectionLostException
*/
public void close(ConnectionEndMessage message) throws ConnectionLostException {
interrupt();
networkAccessProtocol.closeConnection(message);
}
/**
* Closes the channel hardly. Doesn't wait for anything.
*/
public void forceClose() {
interrupt();
networkAccessProtocol.forceClose();
}
/**
* AIRunner.Interface for users of a network channel.
*/
public interface NetworkMessageReceivedHandle<M> {
/**
* Handles incoming messages from the network.
*
* @param message the message that was received
*/
public void handleMessage(M message) throws NotConnectedException, ConnectionLostException;
/**
* Handles the exceptions that can occur when dealing with (mostly) network errors.
* Note: the exception is not thrown, but given as parameter!
*
* @param exception the exception to handle. Will be one of: ConnectionLostException, TechnicalException
*/
public void handleException(Exception exception);
}
}