From 9aa87bce7fdd8f3b71ebab5dbd2b5053b7cc6357 Mon Sep 17 00:00:00 2001 From: Younes Date: Thu, 26 Feb 2026 10:21:38 +0100 Subject: [PATCH 1/8] Added logging in TCP and improved exception handling --- src/main/java/org/example/TcpServer.java | 71 +++++++++++++++--------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/example/TcpServer.java b/src/main/java/org/example/TcpServer.java index e0a3655d..8309d3d4 100644 --- a/src/main/java/org/example/TcpServer.java +++ b/src/main/java/org/example/TcpServer.java @@ -4,14 +4,17 @@ import java.io.IOException; import java.io.OutputStream; -import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; -import java.nio.charset.StandardCharsets; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; public class TcpServer { + // Använder Logger istället för System.out + private static final Logger logger = Logger.getLogger(TcpServer.class.getName()); + private final int port; private final ConnectionFactory connectionFactory; @@ -21,55 +24,73 @@ public TcpServer(int port, ConnectionFactory connectionFactory) { } public void start() { - System.out.println("Starting TCP server on port " + port); + logger.log(Level.INFO, "Starting TCP server on port {0}", port); try (ServerSocket serverSocket = new ServerSocket(port)) { - while (true) { - Socket clientSocket = serverSocket.accept(); // block - System.out.println("Client connected: " + clientSocket.getRemoteSocketAddress()); - Thread.ofVirtual().start(() -> handleClient(clientSocket)); + while (!Thread.currentThread().isInterrupted()) { + Socket clientSocket = serverSocket.accept(); + + // Lambda för att skjuta upp strängbygget + logger.log(Level.INFO, () -> "Client connected: " + clientSocket.getRemoteSocketAddress()); + + try { + clientSocket.setSoTimeout(10000); + Thread.ofVirtual().start(() -> handleClient(clientSocket)); + } catch (Exception e) { + logger.log(Level.SEVERE, "Could not start thread for client", e); + closeQuietly(clientSocket); + } } } catch (IOException e) { - throw new RuntimeException("Failed to start TCP server", e); + // Kastar IllegalStateException istället för generic RuntimeException + throw new IllegalStateException("Server socket failed unexpectedly", e); } } protected void handleClient(Socket client) { - try(client){ + try (client) { processRequest(client); + } catch (IOException e) { + logger.log(Level.WARNING, () -> "Network error with client: " + e.getMessage()); } catch (Exception e) { - throw new RuntimeException("Failed to close socket", e); + logger.log(Level.SEVERE, "Unexpected error handling client", e); } } - private void processRequest(Socket client) throws Exception { - ConnectionHandler handler = null; - try{ - handler = connectionFactory.create(client); + private void processRequest(Socket client) { + // Använder try-with-resources på ConnectionHandler (AutoCloseable) + try (ConnectionHandler handler = connectionFactory.create(client)) { handler.runConnectionHandler(); } catch (Exception e) { + logger.log(Level.SEVERE, "Failed to process request", e); handleInternalServerError(client); - } finally { - if(handler != null) - handler.close(); } } + private void handleInternalServerError(Socket client) { + if (client.isClosed()) return; - private void handleInternalServerError(Socket client){ HttpResponseBuilder response = new HttpResponseBuilder(); response.setStatusCode(HttpResponseBuilder.SC_INTERNAL_SERVER_ERROR); response.setHeaders(Map.of("Content-Type", "text/plain; charset=utf-8")); response.setBody("⚠️ Internal Server Error 500 ⚠️"); - if (!client.isClosed()) { + try { + OutputStream out = client.getOutputStream(); + out.write(response.build()); + out.flush(); + } catch (IOException _) { + // Unnamed pattern (_) + } + } + + private void closeQuietly(Socket socket) { + if (socket != null) { try { - OutputStream out = client.getOutputStream(); - out.write(response.build()); - out.flush(); - } catch (IOException e) { - System.err.println("Failed to send 500 response: " + e.getMessage()); + socket.close(); + } catch (IOException _) { + // Fix: Unnamed pattern (_) } } } -} +} \ No newline at end of file From f33fb61f8e5e08fcd933c89ff0e4ba5d7c1f4e78 Mon Sep 17 00:00:00 2001 From: Younes Date: Thu, 26 Feb 2026 10:39:52 +0100 Subject: [PATCH 2/8] Added coderabbits suggestion for thread interruption --- src/main/java/org/example/TcpServer.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/example/TcpServer.java b/src/main/java/org/example/TcpServer.java index 8309d3d4..7a96ae84 100644 --- a/src/main/java/org/example/TcpServer.java +++ b/src/main/java/org/example/TcpServer.java @@ -12,7 +12,7 @@ public class TcpServer { - // Använder Logger istället för System.out + // Använder Logger istället för System.out/err private static final Logger logger = Logger.getLogger(TcpServer.class.getName()); private final int port; @@ -24,19 +24,20 @@ public TcpServer(int port, ConnectionFactory connectionFactory) { } public void start() { - logger.log(Level.INFO, "Starting TCP server on port {0}", port); + // Lambda för att skjuta upp strängbygget (Deferred execution) + logger.log(Level.INFO, () -> "Starting TCP server on port " + port); try (ServerSocket serverSocket = new ServerSocket(port)) { while (!Thread.currentThread().isInterrupted()) { Socket clientSocket = serverSocket.accept(); - // Lambda för att skjuta upp strängbygget logger.log(Level.INFO, () -> "Client connected: " + clientSocket.getRemoteSocketAddress()); try { clientSocket.setSoTimeout(10000); Thread.ofVirtual().start(() -> handleClient(clientSocket)); } catch (Exception e) { + // Hanterar misslyckad trådstart för att undvika resursläckor logger.log(Level.SEVERE, "Could not start thread for client", e); closeQuietly(clientSocket); } @@ -48,6 +49,7 @@ public void start() { } protected void handleClient(Socket client) { + // try-with-resources på variabeln stänger socket automatiskt try (client) { processRequest(client); } catch (IOException e) { @@ -58,7 +60,6 @@ protected void handleClient(Socket client) { } private void processRequest(Socket client) { - // Använder try-with-resources på ConnectionHandler (AutoCloseable) try (ConnectionHandler handler = connectionFactory.create(client)) { handler.runConnectionHandler(); } catch (Exception e) { @@ -68,7 +69,7 @@ private void processRequest(Socket client) { } private void handleInternalServerError(Socket client) { - if (client.isClosed()) return; + if (client.isClosed() || !client.isConnected()) return; HttpResponseBuilder response = new HttpResponseBuilder(); response.setStatusCode(HttpResponseBuilder.SC_INTERNAL_SERVER_ERROR); @@ -80,7 +81,7 @@ private void handleInternalServerError(Socket client) { out.write(response.build()); out.flush(); } catch (IOException _) { - // Unnamed pattern (_) + // Unnamed pattern (_) för att markera att felet medvetet ignoreras } } From e9fc27856d43d7855bb3d65c74581e5b4422e170 Mon Sep 17 00:00:00 2001 From: Younes Date: Thu, 26 Feb 2026 10:47:10 +0100 Subject: [PATCH 3/8] Adjusted test for new TCPserver class --- src/test/java/org/example/TcpServerTest.java | 21 +++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/example/TcpServerTest.java b/src/test/java/org/example/TcpServerTest.java index a62b3e95..d89c9be7 100644 --- a/src/test/java/org/example/TcpServerTest.java +++ b/src/test/java/org/example/TcpServerTest.java @@ -14,7 +14,7 @@ class TcpServerTest { @Test - void failedClientRequestShouldReturnError500() throws Exception{ + void failedClientRequestShouldReturnError500() throws Exception { ConnectionFactory mockFactory = Mockito.mock(ConnectionFactory.class); ConnectionHandler mockHandler = Mockito.mock(ConnectionHandler.class); TcpServer server = new TcpServer(0, mockFactory); @@ -22,19 +22,30 @@ void failedClientRequestShouldReturnError500() throws Exception{ Socket mockSocket = Mockito.mock(Socket.class); java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream(); + // Setup mocks when(mockSocket.getOutputStream()).thenReturn(outputStream); + when(mockSocket.isConnected()).thenReturn(true); + when(mockSocket.isClosed()).thenReturn(false); when(mockFactory.create(any(Socket.class))).thenReturn(mockHandler); + // Simulera krasch i handlern Mockito.doThrow(new RuntimeException("Simulated Crash")) .when(mockHandler).runConnectionHandler(); + // Kör metoden server.handleClient(mockSocket); - String response = outputStream.toString(); + // Konvertera output till sträng (använd UTF-8 för att vara säker) + String response = outputStream.toString(java.nio.charset.StandardCharsets.UTF_8); + + // Logga gärna ut vad responsen faktiskt innehåller om det fortsätter strula: + // System.out.println("Actual Response: " + response); + assertAll( - () -> assertTrue(response.contains("500")), - () -> assertTrue(response.contains("Internal Server Error 500")), - () -> assertTrue(response.contains("Content-Type: text/plain")) + // Vi kollar efter delar av HTTP-statusraden och bodyn + () -> assertTrue(response.contains("500"), "Response should contain status code 500"), + () -> assertTrue(response.contains("Internal Server Error"), "Response should contain error message"), + () -> assertTrue(response.contains("Content-Type"), "Response should contain Content-Type header") ); } } From bc4fb278663a23839f9f479047f72a79fc4d83b1 Mon Sep 17 00:00:00 2001 From: Younes Date: Thu, 26 Feb 2026 13:04:07 +0100 Subject: [PATCH 4/8] Removed logging system per colleagues recommendation and kept exception handling and other changes --- src/main/java/org/example/TcpServer.java | 39 +++++++++--------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/example/TcpServer.java b/src/main/java/org/example/TcpServer.java index 7a96ae84..9d9d1f0e 100644 --- a/src/main/java/org/example/TcpServer.java +++ b/src/main/java/org/example/TcpServer.java @@ -7,14 +7,9 @@ import java.net.ServerSocket; import java.net.Socket; import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; public class TcpServer { - // Använder Logger istället för System.out/err - private static final Logger logger = Logger.getLogger(TcpServer.class.getName()); - private final int port; private final ConnectionFactory connectionFactory; @@ -24,51 +19,47 @@ public TcpServer(int port, ConnectionFactory connectionFactory) { } public void start() { - // Lambda för att skjuta upp strängbygget (Deferred execution) - logger.log(Level.INFO, () -> "Starting TCP server on port " + port); - try (ServerSocket serverSocket = new ServerSocket(port)) { while (!Thread.currentThread().isInterrupted()) { Socket clientSocket = serverSocket.accept(); - logger.log(Level.INFO, () -> "Client connected: " + clientSocket.getRemoteSocketAddress()); - try { clientSocket.setSoTimeout(10000); Thread.ofVirtual().start(() -> handleClient(clientSocket)); - } catch (Exception e) { - // Hanterar misslyckad trådstart för att undvika resursläckor - logger.log(Level.SEVERE, "Could not start thread for client", e); + } catch (Exception _) { + // VIKTIGT: Här stänger vi socketen om tråden dör, + // även om vi inte loggar lokalt. closeQuietly(clientSocket); } } } catch (IOException e) { - // Kastar IllegalStateException istället för generic RuntimeException - throw new IllegalStateException("Server socket failed unexpectedly", e); + // SonarQube S112: Kasta specifikt exception istället för generic RuntimeException + throw new IllegalStateException("TCP Server failed to remain open on port " + port, e); } } protected void handleClient(Socket client) { - // try-with-resources på variabeln stänger socket automatiskt + // try-with-resources garanterar att socket stängs try (client) { processRequest(client); - } catch (IOException e) { - logger.log(Level.WARNING, () -> "Network error with client: " + e.getMessage()); - } catch (Exception e) { - logger.log(Level.SEVERE, "Unexpected error handling client", e); + } catch (Exception _) { + // Felhantering sker i processRequest, + // men vi fångar upp eventuella stängningsfel här. } } private void processRequest(Socket client) { + // ConnectionHandler stängs automatiskt try (ConnectionHandler handler = connectionFactory.create(client)) { handler.runConnectionHandler(); - } catch (Exception e) { - logger.log(Level.SEVERE, "Failed to process request", e); + } catch (Exception _) { + // Om något går snett i logiken skickar vi 500-svaret handleInternalServerError(client); } } private void handleInternalServerError(Socket client) { + // Säkerhetsspärr: Skriv bara om socket lever if (client.isClosed() || !client.isConnected()) return; HttpResponseBuilder response = new HttpResponseBuilder(); @@ -81,7 +72,7 @@ private void handleInternalServerError(Socket client) { out.write(response.build()); out.flush(); } catch (IOException _) { - // Unnamed pattern (_) för att markera att felet medvetet ignoreras + // Unnamed pattern (_) - Java 21 standard för ignorerade fel } } @@ -90,7 +81,7 @@ private void closeQuietly(Socket socket) { try { socket.close(); } catch (IOException _) { - // Fix: Unnamed pattern (_) + // Tyst stängning för att förhindra krasch under felhantering } } } From c0bd68b3f17b2e993c9ac5b50ae6a6e2edddb154 Mon Sep 17 00:00:00 2001 From: Younes Date: Thu, 26 Feb 2026 13:33:10 +0100 Subject: [PATCH 5/8] Changed method to try-catch-finally and removed logic to add room for colleagues logging system --- src/main/java/org/example/TcpServer.java | 42 ++++++++++++++++-------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/example/TcpServer.java b/src/main/java/org/example/TcpServer.java index 9d9d1f0e..99a66b4f 100644 --- a/src/main/java/org/example/TcpServer.java +++ b/src/main/java/org/example/TcpServer.java @@ -27,40 +27,54 @@ public void start() { clientSocket.setSoTimeout(10000); Thread.ofVirtual().start(() -> handleClient(clientSocket)); } catch (Exception _) { - // VIKTIGT: Här stänger vi socketen om tråden dör, - // även om vi inte loggar lokalt. + // Om tråden inte kan starta, stäng direkt för att inte läcka closeQuietly(clientSocket); } } } catch (IOException e) { - // SonarQube S112: Kasta specifikt exception istället för generic RuntimeException - throw new IllegalStateException("TCP Server failed to remain open on port " + port, e); + throw new IllegalStateException("TCP Server failed on port " + port, e); } } protected void handleClient(Socket client) { - // try-with-resources garanterar att socket stängs - try (client) { + try { + // Kör logiken först processRequest(client); } catch (Exception _) { - // Felhantering sker i processRequest, - // men vi fångar upp eventuella stängningsfel här. + // Eventuella oväntade fel fångas här (loggas här med nya system) + } finally { + closeQuietly(client); } } private void processRequest(Socket client) { - // ConnectionHandler stängs automatiskt - try (ConnectionHandler handler = connectionFactory.create(client)) { + ConnectionHandler handler = null; + try { + // Skapa handlern manuellt (ingen try-with-resources här heller) + handler = connectionFactory.create(client); handler.runConnectionHandler(); } catch (Exception _) { - // Om något går snett i logiken skickar vi 500-svaret + // 1. Logga felet (loggning system) + + // 2. Skicka 500-svar (Socketen är fortfarande öppen här!) handleInternalServerError(client); + } finally { + // 3. Stäng handlern manuellt + if (handler != null) { + try { + handler.close(); + } catch (Exception _) { + // Ignorera fel vid stängning av handlern + } + } } } private void handleInternalServerError(Socket client) { - // Säkerhetsspärr: Skriv bara om socket lever - if (client.isClosed() || !client.isConnected()) return; + // Kolla om vi kan prata med klienten + if (client.isClosed() || !client.isConnected()) { + return; + } HttpResponseBuilder response = new HttpResponseBuilder(); response.setStatusCode(HttpResponseBuilder.SC_INTERNAL_SERVER_ERROR); @@ -72,7 +86,7 @@ private void handleInternalServerError(Socket client) { out.write(response.build()); out.flush(); } catch (IOException _) { - // Unnamed pattern (_) - Java 21 standard för ignorerade fel + // Ignorera nätverksfel vid sändning } } From 309e204dcf85c7d9818b2bca124de0ecf6e30e9a Mon Sep 17 00:00:00 2001 From: Younes Date: Thu, 26 Feb 2026 13:45:32 +0100 Subject: [PATCH 6/8] Reverted some changes to main version to comply with colleagues PR --- src/main/java/org/example/TcpServer.java | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/example/TcpServer.java b/src/main/java/org/example/TcpServer.java index 99a66b4f..6f0534df 100644 --- a/src/main/java/org/example/TcpServer.java +++ b/src/main/java/org/example/TcpServer.java @@ -27,7 +27,6 @@ public void start() { clientSocket.setSoTimeout(10000); Thread.ofVirtual().start(() -> handleClient(clientSocket)); } catch (Exception _) { - // Om tråden inte kan starta, stäng direkt för att inte läcka closeQuietly(clientSocket); } } @@ -37,41 +36,31 @@ public void start() { } protected void handleClient(Socket client) { - try { - // Kör logiken först + try (client) { processRequest(client); } catch (Exception _) { - // Eventuella oväntade fel fångas här (loggas här med nya system) - } finally { - closeQuietly(client); } } private void processRequest(Socket client) { ConnectionHandler handler = null; try { - // Skapa handlern manuellt (ingen try-with-resources här heller) handler = connectionFactory.create(client); handler.runConnectionHandler(); } catch (Exception _) { - // 1. Logga felet (loggning system) - - // 2. Skicka 500-svar (Socketen är fortfarande öppen här!) handleInternalServerError(client); } finally { - // 3. Stäng handlern manuellt if (handler != null) { try { handler.close(); } catch (Exception _) { - // Ignorera fel vid stängning av handlern } } } } private void handleInternalServerError(Socket client) { - // Kolla om vi kan prata med klienten + // Kontrollera att vi kan skriva till klienten if (client.isClosed() || !client.isConnected()) { return; } @@ -86,7 +75,7 @@ private void handleInternalServerError(Socket client) { out.write(response.build()); out.flush(); } catch (IOException _) { - // Ignorera nätverksfel vid sändning + // Ignorera nätverksfel vid sändning av felmeddelandet } } @@ -95,7 +84,7 @@ private void closeQuietly(Socket socket) { try { socket.close(); } catch (IOException _) { - // Tyst stängning för att förhindra krasch under felhantering + // Tyst stängning } } } From 19e8913cab855276427c8424e3410eda80fd5a58 Mon Sep 17 00:00:00 2001 From: Younes Date: Thu, 26 Feb 2026 14:15:38 +0100 Subject: [PATCH 7/8] Removed error messages for fill in, added coderabbit suggestion in handleInternalServerError method --- src/main/java/org/example/TcpServer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/example/TcpServer.java b/src/main/java/org/example/TcpServer.java index 6f0534df..17d70325 100644 --- a/src/main/java/org/example/TcpServer.java +++ b/src/main/java/org/example/TcpServer.java @@ -39,6 +39,7 @@ protected void handleClient(Socket client) { try (client) { processRequest(client); } catch (Exception _) { + // Ska fyllas in med nya pr } } @@ -54,6 +55,7 @@ private void processRequest(Socket client) { try { handler.close(); } catch (Exception _) { + // ska fyllas in med nya PR } } } @@ -61,7 +63,7 @@ private void processRequest(Socket client) { private void handleInternalServerError(Socket client) { // Kontrollera att vi kan skriva till klienten - if (client.isClosed() || !client.isConnected()) { + if (client.isClosed() || !client.isConnected() || client.isOutputShutdown()) { return; } From 272bc8a023a69d4f558ac9833de867681cd2595e Mon Sep 17 00:00:00 2001 From: Younes Date: Fri, 27 Feb 2026 12:34:00 +0100 Subject: [PATCH 8/8] Created seperate methods for start method as per coderabbits suggestion --- src/main/java/org/example/TcpServer.java | 60 ++++++++++++++++-------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/example/TcpServer.java b/src/main/java/org/example/TcpServer.java index 17d70325..c9fd371f 100644 --- a/src/main/java/org/example/TcpServer.java +++ b/src/main/java/org/example/TcpServer.java @@ -6,6 +6,7 @@ import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketTimeoutException; import java.util.Map; public class TcpServer { @@ -20,26 +21,43 @@ public TcpServer(int port, ConnectionFactory connectionFactory) { public void start() { try (ServerSocket serverSocket = new ServerSocket(port)) { + // Tillåt avbrott genom timeout + serverSocket.setSoTimeout(1000); + while (!Thread.currentThread().isInterrupted()) { - Socket clientSocket = serverSocket.accept(); - - try { - clientSocket.setSoTimeout(10000); - Thread.ofVirtual().start(() -> handleClient(clientSocket)); - } catch (Exception _) { - closeQuietly(clientSocket); - } + acceptAndHandleClient(serverSocket); } } catch (IOException e) { throw new IllegalStateException("TCP Server failed on port " + port, e); } } + private void acceptAndHandleClient(ServerSocket serverSocket) { + try { + Socket clientSocket = serverSocket.accept(); + startClientTask(clientSocket); + } catch (SocketTimeoutException _) { + // Normal timeout för att checka interrupt-flaggan + } catch (IOException _) { + // Will be logged with when new system is integrated + } + } + + private void startClientTask(Socket clientSocket) { + try { + clientSocket.setSoTimeout(10000); + Thread.ofVirtual().start(() -> handleClient(clientSocket)); + } catch (Exception _) { + // Om tråden inte kan startas, stäng socketen direkt + closeQuietly(clientSocket); + } + } + protected void handleClient(Socket client) { try (client) { processRequest(client); } catch (Exception _) { - // Ska fyllas in med nya pr + // Plats för framtida loggning } } @@ -51,18 +69,12 @@ private void processRequest(Socket client) { } catch (Exception _) { handleInternalServerError(client); } finally { - if (handler != null) { - try { - handler.close(); - } catch (Exception _) { - // ska fyllas in med nya PR - } - } + closeHandler(handler); } } private void handleInternalServerError(Socket client) { - // Kontrollera att vi kan skriva till klienten + // Fix för CodeRabbit: Dubbelkolla att output inte är stängd if (client.isClosed() || !client.isConnected() || client.isOutputShutdown()) { return; } @@ -77,7 +89,17 @@ private void handleInternalServerError(Socket client) { out.write(response.build()); out.flush(); } catch (IOException _) { - // Ignorera nätverksfel vid sändning av felmeddelandet + // Plats för framtida loggning + } + } + + private void closeHandler(ConnectionHandler handler) { + if (handler != null) { + try { + handler.close(); + } catch (Exception _) { + // Tyst stängning av handler + } } } @@ -86,7 +108,7 @@ private void closeQuietly(Socket socket) { try { socket.close(); } catch (IOException _) { - // Tyst stängning + // Tyst stängning av socket } } }