package games.strategy.net.nio;
import java.io.IOException;
import java.net.Socket;
import java.nio.channels.SocketChannel;
import java.util.logging.Level;
import java.util.logging.Logger;
import games.strategy.net.INode;
import games.strategy.net.IObjectStreamFactory;
import games.strategy.net.MessageHeader;
/**
* The threads needed for a group of sockets using NIO.
* One thread reds socket data, one thread writes socket data
* and one thread deserializes (decodes) packets read by the read
* thread.
* serializing (encoding) objects to be written across the network is done
* by threads calling this object.
*/
public class NIOSocket implements IErrorReporter {
private static final Logger s_logger = Logger.getLogger(NIOSocket.class.getName());
private final Encoder m_encoder;
private final Decoder m_decoder;
private final NIOWriter m_writer;
private final NIOReader m_reader;
private final NIOSocketListener m_listener;
public NIOSocket(final IObjectStreamFactory factory, final NIOSocketListener listener, final String name) {
m_listener = listener;
m_writer = new NIOWriter(this, name);
m_reader = new NIOReader(this, name);
m_decoder = new Decoder(this, m_reader, this, factory, name);
m_encoder = new Encoder(this, m_writer, factory);
}
INode getLocalNode() {
return m_listener.getLocalNode();
}
INode getRemoteNode(final SocketChannel channel) {
return m_listener.getRemoteNode(channel);
}
/**
* Stop our threads.
* This does not close the sockets we are connected to.
*/
public void shutDown() {
m_writer.shutDown();
m_reader.shutDown();
m_decoder.shutDown();
}
public void send(final SocketChannel to, final MessageHeader header) {
if (to == null) {
throw new IllegalArgumentException("to cant be null!");
}
if (header == null) {
throw new IllegalArgumentException("header cant be null");
}
m_encoder.write(to, header);
}
/**
* Add this channel.
* The channel will either be unquarantined, or an error will be reported
*/
public void add(final SocketChannel channel, final QuarantineConversation conversation) {
if (channel.isBlocking()) {
throw new IllegalArgumentException("Channel is blocking");
}
// add the decoder first, so it can quarantine the messages!
m_decoder.add(channel, conversation);
m_reader.add(channel);
}
void unquarantine(final SocketChannel channel, final QuarantineConversation conversation) {
m_listener.socketUnqaurantined(channel, conversation);
}
@Override
public void error(final SocketChannel channel, final Exception e) {
close(channel);
m_listener.socketError(channel, e);
}
/**
* Close the channel, and clean up any data associated with it.
*/
public void close(final SocketChannel channel) {
try {
final Socket s = channel.socket();
if (!s.isInputShutdown()) {
s.shutdownInput();
}
if (!s.isOutputShutdown()) {
s.shutdownOutput();
}
if (!s.isClosed()) {
s.close();
}
channel.close();
} catch (final IOException e1) {
s_logger.log(Level.FINE, "error closing channel", e1);
}
m_decoder.closed(channel);
m_writer.closed(channel);
m_reader.closed(channel);
}
void messageReceived(final MessageHeader header, final SocketChannel channel) {
m_listener.messageReceived(header, channel);
}
}