package games.strategy.net.nio; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectOutputStream; import java.nio.channels.SocketChannel; import java.util.logging.Level; import java.util.logging.Logger; import games.strategy.net.IObjectStreamFactory; import games.strategy.net.MessageHeader; import games.strategy.net.Node; /** * Encodes data to be written by a writer. */ public class Encoder { private static final Logger s_logger = Logger.getLogger(Encoder.class.getName()); private final NIOWriter m_writer; private final IObjectStreamFactory m_objectStreamFactory; private final NIOSocket m_nioSocket; public Encoder(final NIOSocket nioSocket, final NIOWriter writer, final IObjectStreamFactory objectStreamFactory) { m_nioSocket = nioSocket; m_writer = writer; m_objectStreamFactory = objectStreamFactory; } public void write(final SocketChannel to, final MessageHeader header) { if (s_logger.isLoggable(Level.FINEST)) { s_logger.log(Level.FINEST, "Encoding msg:" + header + " to:" + to); } if (header.getFrom() == null) { throw new IllegalArgumentException("No from node"); } if (to == null) { throw new IllegalArgumentException("No to channel!"); } final ByteArrayOutputStream2 sink = new ByteArrayOutputStream2(512); SocketWriteData data; try { write(header, m_objectStreamFactory.create(sink), to); data = new SocketWriteData(sink.getBuffer(), sink.size()); } catch (final Exception e) { // we arent doing any io, just writing in memory // so something is very wrong s_logger.log(Level.SEVERE, "Error writing object:" + header, e); return; } if (s_logger.isLoggable(Level.FINER)) { s_logger.log(Level.FINER, "encoded msg:" + header.getMessage() + " size:" + data.size()); } m_writer.enque(data, to); } private void write(final MessageHeader header, final ObjectOutputStream out, final SocketChannel remote) throws IOException { if (header.getFrom() == null) { throw new IllegalArgumentException("null from"); } // a broadcast if (header.getFor() == null) { out.write(1); } else { // to a node out.write(0); // the common case, skip writing the address if (header.getFor().equals(m_nioSocket.getRemoteNode(remote))) { out.write(1); } else { // this message is going to be relayed, write the destination out.write(0); ((Node) header.getFor()).writeExternal(out); } } if (header.getFrom().equals(m_nioSocket.getLocalNode())) { out.write(1); } else if (m_nioSocket.getLocalNode() == null) { out.write(2); } else { out.write(0); ((Node) header.getFrom()).writeExternal(out); } final byte type = Decoder.getType(header.getMessage()); out.write(type); if (type != Byte.MAX_VALUE) { ((Externalizable) header.getMessage()).writeExternal(out); } else { out.writeObject(header.getMessage()); } out.reset(); } }