package com.github.luohaha.handler; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Queue; import java.util.logging.Logger; import com.github.luohaha.connection.Connection; import com.github.luohaha.connection.DataBag; import com.github.luohaha.context.Context; import com.github.luohaha.context.ContextBean; import com.github.luohaha.inter.OnClose; import com.github.luohaha.inter.OnRead; import com.github.luohaha.inter.OnWrite; public class IoHandler { private Logger logger = Logger.getLogger("LightComm4J"); private Context context; private Selector selector; private static final int BUFFER_SIZE = 1024; public IoHandler(Selector selector, Context context) { super(); this.context = context; this.selector = selector; } /** * read data from remote site by channel * * @param channel * channel * @param onRead * onRead * @param onClose * onClose * @throws IOException * ioexception */ public void readDataFromRemoteSite(SocketChannel channel, OnRead onRead, OnClose onClose) throws IOException { // store current data ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE); ContextBean bean = this.context.getChanToContextBean().get(channel); // read from remote side int count = channel.read(buffer); if (count >= 0) { // set buffer's position to 0 buffer.flip(); while (buffer.hasRemaining()) { DataBag bag = bean.getReadyToRead(); bag.readFrom(buffer); if (bag.isFinish()) { // finish read one data bag bean.getAlreadyReadData().add(bag.getBytes()); bean.setReadyToRead(new DataBag()); } } // call user's custom function Queue<byte[]> dataQueue = bean.getAlreadyReadData(); while (!dataQueue.isEmpty()) { onRead.onRead(bean.getConnection(), dataQueue.poll()); } } else { // read end try { closeRead(channel); } catch (ClosedChannelException e) { this.logger.warning("[Read Event] : " + e.toString()); throw new IOException(); } if (onClose != null) onClose.onClose(bean.getConnection()); } } /** * write data to remote site * * @param channel * channnel * @param onWrite * onWrite * @throws IOException * IOException */ public void writeDataToRemoteSite(SocketChannel channel, OnWrite onWrite) throws IOException { ContextBean bean = this.context.getChanToContextBean().get(channel); Connection connection = bean.getConnection(); // call write function when user define such function and haven't call // it yet! if (onWrite != null && !connection.isOnWriteCalled()) { connection.setOnWriteCalled(true); onWrite.onWrite(connection); } ByteBuffer buffer = connection.getReadyToWrite().peek(); if (buffer != null) { if (buffer.hasRemaining()) { channel.write(buffer); } // if this buffer finish write to buffer, delete it from queue if (!buffer.hasRemaining()) { connection.getReadyToWrite().poll(); } } // nothing to write if (connection.getReadyToWrite().isEmpty()) { try { closeWrite(channel); } catch (ClosedChannelException e) { this.logger.warning("[Write Event] : " + e.toString()); throw new IOException(); } if (connection.isReadyToClose()) { connection.doClose(); } return; } } /** * close write event * * @param channel * channel * @throws ClosedChannelException * ClosedChannelException */ private void closeWrite(SocketChannel channel) throws ClosedChannelException { closeOps(channel, SelectionKey.OP_WRITE); } /** * close read event * * @param channel * channel * @throws ClosedChannelException * ClosedChannelException */ private void closeRead(SocketChannel channel) throws ClosedChannelException { closeOps(channel, SelectionKey.OP_READ); } /** * close some operations * * @param channel * channel * @param opsToClose * opsToClose * @throws ClosedChannelException * ClosedChannelException */ private void closeOps(SocketChannel channel, int opsToClose) throws ClosedChannelException { ContextBean bean = this.context.getChanToContextBean().get(channel); int ops = bean.getOps(); ops = (~opsToClose) & ops; bean.setOps(ops); channel.register(this.selector, ops); } }