Loading src/main/java/ch15/SimpleChatClient.java +27 −56 Changes for src/main/java/ch15/SimpleChatClient.java: 27 added lines, 56 removed lines. Original line number Diff line number Diff line Loading @@ -9,34 +9,18 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SimpleChatClient { private JTextArea incoming; private JTextField outgoing; private BufferedReader reader; private PrintWriter writer; public static void main(String[] args) throws IOException { Socket socket = setUpNetworking(); ClientUI clientUI = new ClientUI(); clientUI.go(socket); IncomingReader incomingReader = new IncomingReader(clientUI.getIncoming(), socket); ExecutorService threadExecutor = Executors.newSingleThreadExecutor(); threadExecutor.execute(incomingReader); threadExecutor.shutdown(); } private static Socket setUpNetworking() throws IOException { Socket socket = new Socket("127.0.0.1", 5000); System.out.println("networking established"); return socket; public static void main(String[] args) { new SimpleChatClient().go(); } } class ClientUI { private JTextArea incoming; public void go(Socket socket) { public void go() { JFrame frame = new JFrame("Ludicrously Simple Chat Client"); JPanel mainPanel = new JPanel(); incoming = new JTextArea(15, 50); Loading @@ -46,34 +30,33 @@ class ClientUI { JScrollPane qScroller = new JScrollPane(incoming); qScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); qScroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); JTextField outgoing = new JTextField(20); outgoing = new JTextField(20); JButton sendButton = new JButton("Send"); sendButton.addActionListener(new SendButtonListener(outgoing, socket)); sendButton.addActionListener(new SendButtonListener()); mainPanel.add(qScroller); mainPanel.add(outgoing); mainPanel.add(sendButton); setUpNetworking(); Thread readerThread = new Thread(new IncomingReader()); readerThread.start(); frame.getContentPane().add(BorderLayout.CENTER, mainPanel); frame.setSize(800, 500); frame.setVisible(true); } public JTextArea getIncoming() { return incoming; } public static class SendButtonListener implements ActionListener { private final JTextField outgoing; private final PrintWriter writer; } // close go public SendButtonListener(JTextField outgoingMessage, Socket socket) { private void setUpNetworking() { try { Socket socket = new Socket("127.0.0.1", 5000); InputStreamReader streamReader = new InputStreamReader(socket.getInputStream()); reader = new BufferedReader(streamReader); writer = new PrintWriter(socket.getOutputStream()); } catch (IOException e) { throw new RuntimeException(e); } outgoing = outgoingMessage; System.out.println("networking established"); } catch (IOException ex) { ex.printStackTrace(); } } // close setUpNetworking public class SendButtonListener implements ActionListener { public void actionPerformed(ActionEvent ev) { try { writer.println(outgoing.getText()); Loading @@ -84,31 +67,19 @@ class ClientUI { outgoing.setText(""); outgoing.requestFocus(); } } } class IncomingReader implements Runnable { private final BufferedReader reader; private final JTextArea incomingMessageArea; public IncomingReader(JTextArea incomingMessageArea, Socket socket) throws IOException { this.incomingMessageArea = incomingMessageArea; reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); } } // close inner class public class IncomingReader implements Runnable { public void run() { processMessages(); } private void processMessages() { String message; try { while ((message = reader.readLine()) != null) { System.out.println("read " + message); incomingMessageArea.append(message + "\n"); incoming.append(message + "\n"); } // close while } catch (Exception ex) { ex.printStackTrace(); } } } // close run } // close inner class } // close outer class No newline at end of file src/main/java/ch15/SimpleChatServer.java +42 −48 Changes for src/main/java/ch15/SimpleChatServer.java: 42 added lines, 48 removed lines. Original line number Diff line number Diff line package ch15; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.io.Writer; import java.net.InetSocketAddress; import java.nio.channels.Channels; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Iterator; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SimpleChatServer { private final ExecutorService executorService = Executors.newCachedThreadPool(); ArrayList<Writer> clientOutputStreams; public static void main(String[] args) { new SimpleChatServer().go(); } public void go() { clientOutputStreams = new ArrayList<>(); try { ServerSocket serverSock = new ServerSocket(5000); Broadcaster broadcaster = new Broadcaster(); while (!serverSock.isClosed()) { Socket clientSocket = serverSock.accept(); broadcaster.addClient(clientSocket); executorService.execute(new ClientHandler(clientSocket, broadcaster)); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress("localhost", 5000)); while (true) { SocketChannel clientSocket = serverSocketChannel.accept(); Writer writer = Channels.newWriter(clientSocket, StandardCharsets.UTF_8); clientOutputStreams.add(writer); Thread t = new Thread(new ClientHandler(clientSocket)); t.start(); System.out.println("got a connection"); } executorService.shutdown(); } catch (Exception ex) { ex.printStackTrace(); } } // close go public void tellEveryone(String message) { for (Writer writer : clientOutputStreams) { try { writer.append(message).write("\n"); writer.flush(); } catch (Exception ex) { ex.printStackTrace(); } } } // end while } // close tellEveryone class ClientHandler implements Runnable { private final BufferedReader reader; private final Broadcaster broadcaster; public class ClientHandler implements Runnable { BufferedReader reader; SocketChannel socket; public ClientHandler(Socket clientSocket, Broadcaster broadcaster) throws IOException { this.broadcaster = broadcaster; InputStreamReader isReader = new InputStreamReader(clientSocket.getInputStream()); reader = new BufferedReader(isReader); public ClientHandler(SocketChannel clientSocket) { try { socket = clientSocket; reader = new BufferedReader(Channels.newReader(socket, StandardCharsets.UTF_8)); } catch (Exception ex) { ex.printStackTrace(); } } // close constructor public void run() { Loading @@ -50,31 +64,11 @@ class ClientHandler implements Runnable { try { while ((message = reader.readLine()) != null) { System.out.println("read " + message); broadcaster.tellEveryone(message); tellEveryone(message); } // close while } catch (Exception ex) { ex.printStackTrace(); } } } class Broadcaster { private final ArrayList<PrintWriter> clientOutputStreams = new ArrayList<>(); void addClient(Socket clientSocket) throws IOException { clientOutputStreams.add(new PrintWriter(clientSocket.getOutputStream())); } public void tellEveryone(String message) { Iterator<PrintWriter> it = clientOutputStreams.iterator(); while (it.hasNext()) { try { PrintWriter writer = it.next(); writer.println(message); writer.flush(); } catch (Exception ex) { ex.printStackTrace(); } } } } No newline at end of file } // close run } // close inner class } // close class No newline at end of file src/test/java/ch15/SimpleChatServerTest.java 0 → 100644 +73 −0 Changes for src/test/java/ch15/SimpleChatServerTest.java: 73 added lines, 0 removed lines. Original line number Diff line number Diff line package ch15; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static org.junit.jupiter.api.Assertions.assertTrue; // Needs a SimpleChatServer running (is not a real unit test class, more like an integration test) class SimpleChatServerTest { @Test void testSend() throws IOException { Socket socket = connect(); assertTrue(send(socket, "Hi")); socket.close(); } @Test // this will work if run when there are no other clients connected // this will likely hang if other clients are already connected (e.g. running testReceive) void testSendAndReceive() throws IOException, InterruptedException { Socket socket = connect(); ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.submit(() -> Assertions.assertEquals("Hi", receive(socket))); assertTrue(send(socket, "Hi")); Thread.sleep(10_000); socket.close(); } @Test // NOT a real unit test - run this to have a client sat waiting for a response. You can get this to // pass by sending "Hi" to the server with another client, e.g. by calling testSend. void testReceive() throws IOException { Socket socket = connect(); Assertions.assertEquals("Hi", receive(socket)); socket.close(); } public boolean send(Socket socket, String payload) throws IOException { PrintWriter writer = new PrintWriter(socket.getOutputStream()); writer.println(payload); writer.flush(); return true; } public String receive(Socket socket) { try (InputStreamReader streamReader = new InputStreamReader(socket.getInputStream()); BufferedReader reader = new BufferedReader(streamReader)) { String message = reader.readLine(); System.out.println("read " + message); return message; } catch (IOException e) { e.printStackTrace(); } return null; } private Socket connect() throws IOException { Socket socket = new Socket("127.0.0.1", 5000); System.out.println(socket.getLocalPort() + " connected"); return socket; } } No newline at end of file Loading
src/main/java/ch15/SimpleChatClient.java +27 −56 Changes for src/main/java/ch15/SimpleChatClient.java: 27 added lines, 56 removed lines. Original line number Diff line number Diff line Loading @@ -9,34 +9,18 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SimpleChatClient { private JTextArea incoming; private JTextField outgoing; private BufferedReader reader; private PrintWriter writer; public static void main(String[] args) throws IOException { Socket socket = setUpNetworking(); ClientUI clientUI = new ClientUI(); clientUI.go(socket); IncomingReader incomingReader = new IncomingReader(clientUI.getIncoming(), socket); ExecutorService threadExecutor = Executors.newSingleThreadExecutor(); threadExecutor.execute(incomingReader); threadExecutor.shutdown(); } private static Socket setUpNetworking() throws IOException { Socket socket = new Socket("127.0.0.1", 5000); System.out.println("networking established"); return socket; public static void main(String[] args) { new SimpleChatClient().go(); } } class ClientUI { private JTextArea incoming; public void go(Socket socket) { public void go() { JFrame frame = new JFrame("Ludicrously Simple Chat Client"); JPanel mainPanel = new JPanel(); incoming = new JTextArea(15, 50); Loading @@ -46,34 +30,33 @@ class ClientUI { JScrollPane qScroller = new JScrollPane(incoming); qScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); qScroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); JTextField outgoing = new JTextField(20); outgoing = new JTextField(20); JButton sendButton = new JButton("Send"); sendButton.addActionListener(new SendButtonListener(outgoing, socket)); sendButton.addActionListener(new SendButtonListener()); mainPanel.add(qScroller); mainPanel.add(outgoing); mainPanel.add(sendButton); setUpNetworking(); Thread readerThread = new Thread(new IncomingReader()); readerThread.start(); frame.getContentPane().add(BorderLayout.CENTER, mainPanel); frame.setSize(800, 500); frame.setVisible(true); } public JTextArea getIncoming() { return incoming; } public static class SendButtonListener implements ActionListener { private final JTextField outgoing; private final PrintWriter writer; } // close go public SendButtonListener(JTextField outgoingMessage, Socket socket) { private void setUpNetworking() { try { Socket socket = new Socket("127.0.0.1", 5000); InputStreamReader streamReader = new InputStreamReader(socket.getInputStream()); reader = new BufferedReader(streamReader); writer = new PrintWriter(socket.getOutputStream()); } catch (IOException e) { throw new RuntimeException(e); } outgoing = outgoingMessage; System.out.println("networking established"); } catch (IOException ex) { ex.printStackTrace(); } } // close setUpNetworking public class SendButtonListener implements ActionListener { public void actionPerformed(ActionEvent ev) { try { writer.println(outgoing.getText()); Loading @@ -84,31 +67,19 @@ class ClientUI { outgoing.setText(""); outgoing.requestFocus(); } } } class IncomingReader implements Runnable { private final BufferedReader reader; private final JTextArea incomingMessageArea; public IncomingReader(JTextArea incomingMessageArea, Socket socket) throws IOException { this.incomingMessageArea = incomingMessageArea; reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); } } // close inner class public class IncomingReader implements Runnable { public void run() { processMessages(); } private void processMessages() { String message; try { while ((message = reader.readLine()) != null) { System.out.println("read " + message); incomingMessageArea.append(message + "\n"); incoming.append(message + "\n"); } // close while } catch (Exception ex) { ex.printStackTrace(); } } } // close run } // close inner class } // close outer class No newline at end of file
src/main/java/ch15/SimpleChatServer.java +42 −48 Changes for src/main/java/ch15/SimpleChatServer.java: 42 added lines, 48 removed lines. Original line number Diff line number Diff line package ch15; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.io.Writer; import java.net.InetSocketAddress; import java.nio.channels.Channels; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Iterator; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SimpleChatServer { private final ExecutorService executorService = Executors.newCachedThreadPool(); ArrayList<Writer> clientOutputStreams; public static void main(String[] args) { new SimpleChatServer().go(); } public void go() { clientOutputStreams = new ArrayList<>(); try { ServerSocket serverSock = new ServerSocket(5000); Broadcaster broadcaster = new Broadcaster(); while (!serverSock.isClosed()) { Socket clientSocket = serverSock.accept(); broadcaster.addClient(clientSocket); executorService.execute(new ClientHandler(clientSocket, broadcaster)); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress("localhost", 5000)); while (true) { SocketChannel clientSocket = serverSocketChannel.accept(); Writer writer = Channels.newWriter(clientSocket, StandardCharsets.UTF_8); clientOutputStreams.add(writer); Thread t = new Thread(new ClientHandler(clientSocket)); t.start(); System.out.println("got a connection"); } executorService.shutdown(); } catch (Exception ex) { ex.printStackTrace(); } } // close go public void tellEveryone(String message) { for (Writer writer : clientOutputStreams) { try { writer.append(message).write("\n"); writer.flush(); } catch (Exception ex) { ex.printStackTrace(); } } } // end while } // close tellEveryone class ClientHandler implements Runnable { private final BufferedReader reader; private final Broadcaster broadcaster; public class ClientHandler implements Runnable { BufferedReader reader; SocketChannel socket; public ClientHandler(Socket clientSocket, Broadcaster broadcaster) throws IOException { this.broadcaster = broadcaster; InputStreamReader isReader = new InputStreamReader(clientSocket.getInputStream()); reader = new BufferedReader(isReader); public ClientHandler(SocketChannel clientSocket) { try { socket = clientSocket; reader = new BufferedReader(Channels.newReader(socket, StandardCharsets.UTF_8)); } catch (Exception ex) { ex.printStackTrace(); } } // close constructor public void run() { Loading @@ -50,31 +64,11 @@ class ClientHandler implements Runnable { try { while ((message = reader.readLine()) != null) { System.out.println("read " + message); broadcaster.tellEveryone(message); tellEveryone(message); } // close while } catch (Exception ex) { ex.printStackTrace(); } } } class Broadcaster { private final ArrayList<PrintWriter> clientOutputStreams = new ArrayList<>(); void addClient(Socket clientSocket) throws IOException { clientOutputStreams.add(new PrintWriter(clientSocket.getOutputStream())); } public void tellEveryone(String message) { Iterator<PrintWriter> it = clientOutputStreams.iterator(); while (it.hasNext()) { try { PrintWriter writer = it.next(); writer.println(message); writer.flush(); } catch (Exception ex) { ex.printStackTrace(); } } } } No newline at end of file } // close run } // close inner class } // close class No newline at end of file
src/test/java/ch15/SimpleChatServerTest.java 0 → 100644 +73 −0 Changes for src/test/java/ch15/SimpleChatServerTest.java: 73 added lines, 0 removed lines. Original line number Diff line number Diff line package ch15; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static org.junit.jupiter.api.Assertions.assertTrue; // Needs a SimpleChatServer running (is not a real unit test class, more like an integration test) class SimpleChatServerTest { @Test void testSend() throws IOException { Socket socket = connect(); assertTrue(send(socket, "Hi")); socket.close(); } @Test // this will work if run when there are no other clients connected // this will likely hang if other clients are already connected (e.g. running testReceive) void testSendAndReceive() throws IOException, InterruptedException { Socket socket = connect(); ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.submit(() -> Assertions.assertEquals("Hi", receive(socket))); assertTrue(send(socket, "Hi")); Thread.sleep(10_000); socket.close(); } @Test // NOT a real unit test - run this to have a client sat waiting for a response. You can get this to // pass by sending "Hi" to the server with another client, e.g. by calling testSend. void testReceive() throws IOException { Socket socket = connect(); Assertions.assertEquals("Hi", receive(socket)); socket.close(); } public boolean send(Socket socket, String payload) throws IOException { PrintWriter writer = new PrintWriter(socket.getOutputStream()); writer.println(payload); writer.flush(); return true; } public String receive(Socket socket) { try (InputStreamReader streamReader = new InputStreamReader(socket.getInputStream()); BufferedReader reader = new BufferedReader(streamReader)) { String message = reader.readLine(); System.out.println("read " + message); return message; } catch (IOException e) { e.printStackTrace(); } return null; } private Socket connect() throws IOException { Socket socket = new Socket("127.0.0.1", 5000); System.out.println(socket.getLocalPort() + " connected"); return socket; } } No newline at end of file