class NetworkSession implements Runnable { private final Logger log = LoggerFactory.getLogger(LogName.forObject(this)); private final NetworkManager manager; private final String host; private final int port; private final EventSupport eventSupport; private final ByteBuffer readBuffer = ByteBuffer.allocateDirect(NetworkManager.BUFFER_SIZE); private final StringBuilder sb = new StringBuilder(); private SocketChannel channel; NetworkSession(NetworkManager manager, String host, int port, EventSupport eventSupport) { this.manager = manager; this.host = host; this.port = port; this.eventSupport = eventSupport; } @Override public void run() { log.debug("network thread started"); try { // run once InetSocketAddress address = new InetSocketAddress(host, port); channel = SocketChannel.open(address); channel.configureBlocking(true); reportChannel(); // main loop while (!Thread.currentThread().isInterrupted()) { int bytesRead = channel.read(readBuffer); if (bytesRead == -1) break; // TODO these StringBuilder shenanigans are fake // actually put chars in a CharBuffer and write to parser // parser returns list of events to be dispatched readBuffer.flip(); while (readBuffer.hasRemaining()) { sb.append((char) (readBuffer.get() & 0xFF)); } dispatchEvent(EventType.TEXT_RECEIVED, null, sb.toString()); sb.setLength(0); readBuffer.clear(); } } catch (ClosedByInterruptException e) { // normal result of being interrupted } catch (Throwable t) { log.error("network error", t); dispatchEvent(EventType.NETWORK_ERROR, null, t); } finally { if (channel != null && channel.isOpen()) { try { channel.close(); } catch (IOException e) { // ignore } } reportStatus(NetworkState.DISCONNECTED); log.debug("network thread exiting"); } } /** * Posts state changes to the manager in the UI thread. * * @param status the new state */ private void reportStatus(final NetworkState status) { SwingUtilities.invokeLater( new Runnable() { @Override public void run() { manager.setStatus(status); } }); } /** Posts the channel identity to the manager in the UI thread. */ private void reportChannel() { SwingUtilities.invokeLater( new Runnable() { @Override public void run() { manager.setChannel(channel); manager.setStatus(NetworkState.CONNECTED); } }); } /** * Dispatch a single event in the UI thread. * * @param type * @param oldValue * @param newValue */ private void dispatchEvent(EventType type, Object oldValue, Object newValue) { final WeaponEvent event = new WeaponEvent(manager, type, oldValue, newValue); SwingUtilities.invokeLater( new Runnable() { @Override public void run() { eventSupport.firePropertyChange(event); } }); } }