package ar.com.javacuriosities.nio.server.tasks; 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.util.*; import java.util.concurrent.TimeUnit; import ar.com.javacuriosities.nio.server.clients.ClientSocket; import ar.com.javacuriosities.nio.server.message.Message; import ar.com.javacuriosities.nio.server.message.MessageBuffer; import ar.com.javacuriosities.nio.server.message.processor.IMessageProcessor; import ar.com.javacuriosities.nio.server.message.reader.IMessageReaderFactory; import ar.com.javacuriosities.nio.server.message.writer.MessageWriter; import ar.com.javacuriosities.nio.server.message.writer.WriteProxy; /* * Esta tarea se encarga de procesar los request en un thread especifico */ public class ProcessorTask implements Runnable { private Queue<ClientSocket> clientSocketQueue = null; private MessageBuffer readMessageBuffer = null; private IMessageReaderFactory messageReaderFactory = null; private Queue<Message> outboundMessageQueue = new LinkedList<>(); private ByteBuffer readByteBuffer = ByteBuffer.allocate(1024 * 1024); private ByteBuffer writeByteBuffer = ByteBuffer.allocate(1024 * 1024); private Selector readSelector = null; private Selector writeSelector = null; private WriteProxy writeProxy = null; private IMessageProcessor messageProcessor = null; private long nextSocketId = 16 * 1024; private Map<Long, ClientSocket> clientSocketMapId = new HashMap<>(); private Set<ClientSocket> emptyToNonEmptySockets = new HashSet<>(); private Set<ClientSocket> nonEmptyToEmptySockets = new HashSet<>(); public ProcessorTask(Queue<ClientSocket> clientSocketQueue, MessageBuffer readMessageBuffer, MessageBuffer writeMessageBuffer, IMessageReaderFactory messageReaderFactory, IMessageProcessor messageProcessor) throws IOException { this.clientSocketQueue = clientSocketQueue; this.readMessageBuffer = readMessageBuffer; this.writeProxy = new WriteProxy(writeMessageBuffer, this.outboundMessageQueue); this.messageReaderFactory = messageReaderFactory; this.messageProcessor = messageProcessor; this.readSelector = Selector.open(); this.writeSelector = Selector.open(); } public void run() { while (true) { try { executeCycle(); } catch (IOException e) { e.printStackTrace(); } try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public void executeCycle() throws IOException { takeNewSockets(); readFromSockets(); writeToSockets(); } public void takeNewSockets() throws IOException { ClientSocket clientSocket = this.clientSocketQueue.poll(); while (clientSocket != null) { clientSocket.socketId = this.nextSocketId++; clientSocket.socketChannel.configureBlocking(false); clientSocket.messageReader = this.messageReaderFactory.createMessageReader(); clientSocket.messageReader.init(this.readMessageBuffer); clientSocket.messageWriter = new MessageWriter(); this.clientSocketMapId.put(clientSocket.socketId, clientSocket); SelectionKey key = clientSocket.socketChannel.register(this.readSelector, SelectionKey.OP_READ); key.attach(clientSocket); clientSocket = this.clientSocketQueue.poll(); } } public void readFromSockets() throws IOException { int readReady = this.readSelector.selectNow(); if (readReady > 0) { Set<SelectionKey> selectedKeys = this.readSelector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); readFromSocket(key); keyIterator.remove(); } selectedKeys.clear(); } } public void writeToSockets() throws IOException { // Tomamos todos los mensajes de la "outboundMessageQueue" takeNewOutboundMessages(); // Cancelamos todos los sockets que no tienen mas data para escribir cancelEmptySockets(); // Registramos los sockets que tienen data y aun no fueron registrados registerNonEmptySockets(); int writeReady = this.writeSelector.selectNow(); if (writeReady > 0) { Set<SelectionKey> selectionKeys = this.writeSelector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectionKeys.iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); ClientSocket socket = (ClientSocket) key.attachment(); socket.messageWriter.write(socket, this.writeByteBuffer); if (socket.messageWriter.isEmpty()) { this.nonEmptyToEmptySockets.add(socket); } keyIterator.remove(); } selectionKeys.clear(); } } private void readFromSocket(SelectionKey key) throws IOException { ClientSocket socket = (ClientSocket) key.attachment(); socket.messageReader.read(socket, this.readByteBuffer); List<Message> fullMessages = socket.messageReader.getMessages(); if (fullMessages.size() > 0) { for (Message message : fullMessages) { message.socketId = socket.socketId; this.messageProcessor.process(message, this.writeProxy); } fullMessages.clear(); } if (socket.endOfStreamReached) { System.out.println("Socket closed: " + socket.socketId); this.clientSocketMapId.remove(socket.socketId); key.attach(null); key.cancel(); key.channel().close(); } } private void registerNonEmptySockets() throws ClosedChannelException { for (ClientSocket socket : emptyToNonEmptySockets) { socket.socketChannel.register(this.writeSelector, SelectionKey.OP_WRITE, socket); } emptyToNonEmptySockets.clear(); } private void cancelEmptySockets() { for (ClientSocket socket : nonEmptyToEmptySockets) { SelectionKey key = socket.socketChannel.keyFor(this.writeSelector); key.cancel(); } nonEmptyToEmptySockets.clear(); } private void takeNewOutboundMessages() { Message outMessage = this.outboundMessageQueue.poll(); while (outMessage != null) { ClientSocket socket = this.clientSocketMapId.get(outMessage.socketId); if (socket != null) { MessageWriter messageWriter = socket.messageWriter; if (messageWriter.isEmpty()) { messageWriter.enqueue(outMessage); nonEmptyToEmptySockets.remove(socket); emptyToNonEmptySockets.add(socket); } else { messageWriter.enqueue(outMessage); } } outMessage = this.outboundMessageQueue.poll(); } } }