From f20a5824ae1a50f07dec2e7a0d57c6d9da94e59a Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 30 Nov 2024 07:35:44 +0200 Subject: [PATCH 01/21] Fixed busy waiting in points 1,2,3,7 --- .../logaggregation/LogAggregator.java | 13 +++++++ .../queue/load/leveling/MessageQueue.java | 2 + .../queue/load/leveling/ServiceExecutor.java | 7 ++-- server-session/README.md | 39 ++++++++++++++++--- server-session/pom.xml | 12 ++++++ .../java/com/iluwatar/sessionserver/App.java | 27 +++++++++++-- .../iluwatar/sessionserver/LoginHandler.java | 2 +- .../LogoutHandlerTest.java | 4 +- twin/README.md | 37 +++++++++++++++--- .../java/com/iluwatar/twin/BallThread.java | 30 +++++++++++--- 10 files changed, 148 insertions(+), 25 deletions(-) diff --git a/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java b/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java index 37417e21267d..f84097a3d4b8 100644 --- a/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java +++ b/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java @@ -46,6 +46,7 @@ public class LogAggregator { private final ConcurrentLinkedQueue buffer = new ConcurrentLinkedQueue<>(); private final LogLevel minLogLevel; private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + private final Object bufferWait=new Object(); private final AtomicInteger logCount = new AtomicInteger(0); /** @@ -77,6 +78,7 @@ public void collectLog(LogEntry logEntry) { } buffer.offer(logEntry); + bufferWake(); if (logCount.incrementAndGet() >= BUFFER_THRESHOLD) { flushBuffer(); @@ -109,6 +111,11 @@ private void startBufferFlusher() { executorService.execute(() -> { while (!Thread.currentThread().isInterrupted()) { try { + synchronized (bufferWait) { + if (buffer.isEmpty()) { + bufferWait.wait(); + } + } Thread.sleep(5000); // Flush every 5 seconds. flushBuffer(); } catch (InterruptedException e) { @@ -117,4 +124,10 @@ private void startBufferFlusher() { } }); } + public void bufferWake(){ + synchronized (bufferWait) + { + bufferWait.notify(); + } + } } diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java index 1d28faa54ca5..5c03e7ab1592 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java @@ -36,6 +36,7 @@ public class MessageQueue { private final BlockingQueue blkQueue; + public Object serviceExecutorWait=new Object(); // Default constructor when called creates Blocking Queue object. public MessageQueue() { @@ -50,6 +51,7 @@ public void submitMsg(Message msg) { try { if (null != msg) { blkQueue.add(msg); + serviceExecutorWait.notify(); } } catch (Exception e) { LOGGER.error(e.getMessage()); diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java index 02530042b370..cde46c32296f 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java @@ -32,7 +32,6 @@ */ @Slf4j public class ServiceExecutor implements Runnable { - private final MessageQueue msgQueue; public ServiceExecutor(MessageQueue msgQueue) { @@ -51,9 +50,11 @@ public void run() { LOGGER.info(msg + " is served."); } else { LOGGER.info("Service Executor: Waiting for Messages to serve .. "); + synchronized (msgQueue.serviceExecutorWait) + { + msgQueue.serviceExecutorWait.wait(); + } } - - Thread.sleep(1000); } } catch (Exception e) { LOGGER.error(e.getMessage()); diff --git a/server-session/README.md b/server-session/README.md index 4ce452b53f7d..a03444b6268d 100644 --- a/server-session/README.md +++ b/server-session/README.md @@ -47,26 +47,43 @@ The `main` application starts a server and assigns handlers to manage login and ```java public class App { - private static Map sessions = new HashMap<>(); - private static Map sessionCreationTimes = new HashMap<>(); - private static final long SESSION_EXPIRATION_TIME = 10000; + // Map to store session data (simulated using a HashMap) + private static Map sessions = new HashMap(); + private static Map sessionCreationTimes = new HashMap(); + private static final long SESSION_EXPIRATION_TIME = 10000; + private static Object sessionExpirationWait=new Object(); // used to make expiration task wait or work based on event (login request sent or not) public static void main(String[] args) throws IOException { + // Create HTTP server listening on port 8000 HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0); + // Set up session management endpoints server.createContext("/login", new LoginHandler(sessions, sessionCreationTimes)); server.createContext("/logout", new LogoutHandler(sessions, sessionCreationTimes)); + // Start the server server.start(); + // Start background task to check for expired sessions sessionExpirationTask(); + + LOGGER.info("Server started. Listening on port 8080..."); } private static void sessionExpirationTask() { - new Thread(() -> { + new Thread(() -> { while (true) { try { - Thread.sleep(SESSION_EXPIRATION_TIME); + synchronized (sessions) + { + if(sessions.isEmpty()) + synchronized (sessionExpirationWait) + { + sessionExpirationWait.wait(); // Make Session expiration Checker wait until at least a single login request is sent. + } + } + LOGGER.info("Session expiration checker started..."); + Thread.sleep(SESSION_EXPIRATION_TIME); // Sleep for expiration time Instant currentTime = Instant.now(); synchronized (sessions) { synchronized (sessionCreationTimes) { @@ -75,18 +92,30 @@ public class App { while (iterator.hasNext()) { Map.Entry entry = iterator.next(); if (entry.getValue().plusMillis(SESSION_EXPIRATION_TIME).isBefore(currentTime)) { + LOGGER.info("User " + entry.getValue() + " removed"); sessions.remove(entry.getKey()); iterator.remove(); } } } } + LOGGER.info("Session expiration checker finished!"); } catch (InterruptedException e) { + LOGGER.error("An error occurred: ", e); Thread.currentThread().interrupt(); } } }).start(); } + + public static void expirationTaskWake() //Wake up sleeping Expiration task thread + { + synchronized (sessionExpirationWait) + { + sessionExpirationWait.notify(); + } + } + } ``` diff --git a/server-session/pom.xml b/server-session/pom.xml index e7cdcf82c201..c4a55ba4f2a8 100644 --- a/server-session/pom.xml +++ b/server-session/pom.xml @@ -49,5 +49,17 @@ test + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + \ No newline at end of file diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/App.java b/server-session/src/main/java/com/iluwatar/sessionserver/App.java index a3c66d3ff634..58b566e8f2e8 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/App.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/App.java @@ -33,6 +33,7 @@ import java.util.Map; import lombok.extern.slf4j.Slf4j; + /** * The server session pattern is a behavioral design pattern concerned with assigning the responsibility * of storing session data on the server side. Within the context of stateless protocols like HTTP all @@ -54,9 +55,11 @@ public class App { // Map to store session data (simulated using a HashMap) - private static Map sessions = new HashMap<>(); - private static Map sessionCreationTimes = new HashMap<>(); + + private static Map sessions = new HashMap(); + private static Map sessionCreationTimes = new HashMap(); private static final long SESSION_EXPIRATION_TIME = 10000; + private static Object sessionExpirationWait=new Object(); // used to make expiration task wait or work based on event (login request sent or not) /** * Main entry point. @@ -81,9 +84,17 @@ public static void main(String[] args) throws IOException { } private static void sessionExpirationTask() { - new Thread(() -> { + new Thread(() -> { while (true) { try { + synchronized (sessions) + { + if(sessions.isEmpty()) + synchronized (sessionExpirationWait) + { + sessionExpirationWait.wait(); // Make Session expiration Checker wait until at least a single login request is sent. + } + } LOGGER.info("Session expiration checker started..."); Thread.sleep(SESSION_EXPIRATION_TIME); // Sleep for expiration time Instant currentTime = Instant.now(); @@ -94,6 +105,7 @@ private static void sessionExpirationTask() { while (iterator.hasNext()) { Map.Entry entry = iterator.next(); if (entry.getValue().plusMillis(SESSION_EXPIRATION_TIME).isBefore(currentTime)) { + LOGGER.info("User " + entry.getValue() + " removed"); sessions.remove(entry.getKey()); iterator.remove(); } @@ -108,4 +120,13 @@ private static void sessionExpirationTask() { } }).start(); } + + public static void expirationTaskWake() //Wake up sleeping Expiration task thread + { + synchronized (sessionExpirationWait) + { + sessionExpirationWait.notify(); + } + } + } \ No newline at end of file diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java b/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java index 1e36ac052570..0abea56a1190 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java @@ -60,7 +60,7 @@ public void handle(HttpExchange exchange) { // Set session ID as cookie exchange.getResponseHeaders().add("Set-Cookie", "sessionID=" + sessionId); - + App.expirationTaskWake(); // Wake up expiration task // Send response String response = "Login successful!\n" + "Session ID: " + sessionId; try { diff --git a/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java b/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java index 1b9d817b0114..05ce10c57813 100644 --- a/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java +++ b/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java @@ -69,9 +69,11 @@ public void setUp() { public void testHandler_SessionNotExpired() { //assemble - sessions.put("1234", 1); //Fake login details since LoginHandler isn't called + sessions.put("1234", 1);//Fake login details since LoginHandler isn't called + sessionCreationTimes.put("1234", Instant.now()); //Fake login details since LoginHandler isn't called + App.expirationTaskWake(); when(exchange.getRequestHeaders()).thenReturn(headers); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); when(exchange.getResponseBody()).thenReturn(outputStream); diff --git a/twin/README.md b/twin/README.md index b2913e0a963f..4c51066563f3 100644 --- a/twin/README.md +++ b/twin/README.md @@ -82,21 +82,38 @@ public class BallItem extends GameItem { ```java @Slf4j public class BallThread extends Thread { + @Setter private BallItem twin; + private volatile boolean isSuspended; + private volatile boolean isRunning = true; + private Object lock=new Object(); + + /** + * Run the thread. + */ public void run() { + while (isRunning) { - if (!isSuspended) { + if (isSuspended) { + synchronized (lock) { + try { + lock.wait(); // Wait until resumed :: busy loop fix + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } else { twin.draw(); twin.move(); - } - try { - Thread.sleep(250); - } catch (InterruptedException e) { - throw new RuntimeException(e); + try { + Thread.sleep(250); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } } } } @@ -109,11 +126,19 @@ public class BallThread extends Thread { public void resumeMe() { isSuspended = false; LOGGER.info("Begin to resume BallThread"); + synchronized (lock) + { + lock.notify(); + } } public void stopMe() { this.isRunning = false; this.isSuspended = true; + synchronized (lock) + { + lock.notify(); + } } } ``` diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index 9d4d9cf71a76..8a6b96fe283f 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -42,20 +42,30 @@ public class BallThread extends Thread { private volatile boolean isRunning = true; + private Object lock=new Object(); + /** * Run the thread. */ public void run() { while (isRunning) { - if (!isSuspended) { + if (isSuspended) { + synchronized (lock) { + try { + lock.wait(); //Wait until resumed :: busy loop fix + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } else { twin.draw(); twin.move(); - } - try { - Thread.sleep(250); - } catch (InterruptedException e) { - throw new RuntimeException(e); + try { + Thread.sleep(250); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } } } } @@ -68,11 +78,19 @@ public void suspendMe() { public void resumeMe() { isSuspended = false; LOGGER.info("Begin to resume BallThread"); + synchronized (lock) + { + lock.notify(); + } } public void stopMe() { this.isRunning = false; this.isSuspended = true; + synchronized (lock) + { + lock.notify(); + } } } From 58e5c62e1785c6758df89dae3cc8d980e8f516d7 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 30 Nov 2024 08:24:26 +0200 Subject: [PATCH 02/21] Fixed busy waiting in points 1,2,3,7 (1) --- .../iluwatar/queue/load/leveling/MessageQueue.java | 2 +- server-session/pom.xml | 13 ------------- .../main/java/com/iluwatar/sessionserver/App.java | 12 ++++++------ twin/README.md | 2 +- .../src/main/java/com/iluwatar/twin/BallThread.java | 2 +- 5 files changed, 9 insertions(+), 22 deletions(-) diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java index 5c03e7ab1592..6f7038682147 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java @@ -36,7 +36,7 @@ public class MessageQueue { private final BlockingQueue blkQueue; - public Object serviceExecutorWait=new Object(); + public final Object serviceExecutorWait=new Object(); // Default constructor when called creates Blocking Queue object. public MessageQueue() { diff --git a/server-session/pom.xml b/server-session/pom.xml index c4a55ba4f2a8..530140fccb30 100644 --- a/server-session/pom.xml +++ b/server-session/pom.xml @@ -49,17 +49,4 @@ test - - - - org.apache.maven.plugins - maven-compiler-plugin - - 8 - 8 - - - - - \ No newline at end of file diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/App.java b/server-session/src/main/java/com/iluwatar/sessionserver/App.java index 58b566e8f2e8..314ea335ad99 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/App.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/App.java @@ -59,7 +59,7 @@ public class App { private static Map sessions = new HashMap(); private static Map sessionCreationTimes = new HashMap(); private static final long SESSION_EXPIRATION_TIME = 10000; - private static Object sessionExpirationWait=new Object(); // used to make expiration task wait or work based on event (login request sent or not) + private static final Object sessionExpirationWait=new Object(); // used to make expiration task wait or work based on event (login request sent or not) /** * Main entry point. @@ -89,11 +89,11 @@ private static void sessionExpirationTask() { try { synchronized (sessions) { - if(sessions.isEmpty()) - synchronized (sessionExpirationWait) - { - sessionExpirationWait.wait(); // Make Session expiration Checker wait until at least a single login request is sent. - } + if(sessions.isEmpty()) + synchronized (sessionExpirationWait) + { + sessionExpirationWait.wait(); // Make Session expiration Checker wait until at least a single login request is sent. + } } LOGGER.info("Session expiration checker started..."); Thread.sleep(SESSION_EXPIRATION_TIME); // Sleep for expiration time diff --git a/twin/README.md b/twin/README.md index 4c51066563f3..004b93b714dd 100644 --- a/twin/README.md +++ b/twin/README.md @@ -90,7 +90,7 @@ public class BallThread extends Thread { private volatile boolean isRunning = true; - private Object lock=new Object(); + private final Object lock=new Object(); /** * Run the thread. diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index 8a6b96fe283f..e2d35e07e026 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -42,7 +42,7 @@ public class BallThread extends Thread { private volatile boolean isRunning = true; - private Object lock=new Object(); + private final Object lock=new Object(); /** * Run the thread. From 9415119c0edc8d5236e430651c51f30a75d479a6 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 30 Nov 2024 08:43:48 +0200 Subject: [PATCH 03/21] Checkstyle fix try 1 --- twin/src/main/java/com/iluwatar/twin/BallThread.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index e2d35e07e026..6020891a3732 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -42,7 +42,7 @@ public class BallThread extends Thread { private volatile boolean isRunning = true; - private final Object lock=new Object(); + private final Object lock = new Object(); /** * Run the thread. @@ -78,8 +78,7 @@ public void suspendMe() { public void resumeMe() { isSuspended = false; LOGGER.info("Begin to resume BallThread"); - synchronized (lock) - { + synchronized (lock) { lock.notify(); } } @@ -87,8 +86,7 @@ public void resumeMe() { public void stopMe() { this.isRunning = false; this.isSuspended = true; - synchronized (lock) - { + synchronized (lock) { lock.notify(); } } From 42e4c321258108b4af5819803fccc057b609efb8 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 30 Nov 2024 09:03:03 +0200 Subject: [PATCH 04/21] Checkstyle fix try 2 --- .../logaggregation/LogAggregator.java | 11 ++++--- .../queue/load/leveling/ServiceExecutor.java | 5 ++- .../java/com/iluwatar/sessionserver/App.java | 32 +++++++++---------- .../iluwatar/sessionserver/LoginHandler.java | 3 ++ .../iluwatar/sessionserver/LogoutHandler.java | 3 ++ .../java/com/iluwatar/twin/BallThread.java | 14 ++++++-- 6 files changed, 43 insertions(+), 25 deletions(-) diff --git a/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java b/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java index f84097a3d4b8..06f9fcf3e879 100644 --- a/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java +++ b/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java @@ -46,7 +46,7 @@ public class LogAggregator { private final ConcurrentLinkedQueue buffer = new ConcurrentLinkedQueue<>(); private final LogLevel minLogLevel; private final ExecutorService executorService = Executors.newSingleThreadExecutor(); - private final Object bufferWait=new Object(); + private final Object bufferWait = new Object(); private final AtomicInteger logCount = new AtomicInteger(0); /** @@ -124,9 +124,12 @@ private void startBufferFlusher() { } }); } - public void bufferWake(){ - synchronized (bufferWait) - { + + /** + * Wakes up buffer. + */ + public void bufferWake() { + synchronized (bufferWait) { bufferWait.notify(); } } diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java index cde46c32296f..882ca0b211bf 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java @@ -50,9 +50,8 @@ public void run() { LOGGER.info(msg + " is served."); } else { LOGGER.info("Service Executor: Waiting for Messages to serve .. "); - synchronized (msgQueue.serviceExecutorWait) - { - msgQueue.serviceExecutorWait.wait(); + synchronized (msgQueue.serviceExecutorWait) { + msgQueue.serviceExecutorWait.wait(); } } } diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/App.java b/server-session/src/main/java/com/iluwatar/sessionserver/App.java index 314ea335ad99..1311a573fe42 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/App.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/App.java @@ -56,10 +56,10 @@ public class App { // Map to store session data (simulated using a HashMap) - private static Map sessions = new HashMap(); - private static Map sessionCreationTimes = new HashMap(); + private static Map sessions = new HashMap(); + private static Map sessionCreationTimes = new HashMap(); private static final long SESSION_EXPIRATION_TIME = 10000; - private static final Object sessionExpirationWait=new Object(); // used to make expiration task wait or work based on event (login request sent or not) + private static final Object sessionExpirationWait = new Object(); // used to make expiration task wait or work based on event (login request sent or not) /** * Main entry point. @@ -84,16 +84,15 @@ public static void main(String[] args) throws IOException { } private static void sessionExpirationTask() { - new Thread(() -> { + new Thread(() -> { while (true) { try { - synchronized (sessions) - { - if(sessions.isEmpty()) - synchronized (sessionExpirationWait) - { - sessionExpirationWait.wait(); // Make Session expiration Checker wait until at least a single login request is sent. + synchronized (sessions) { + if (sessions.isEmpty()) { + synchronized (sessionExpirationWait) { + sessionExpirationWait.wait(); } + } } LOGGER.info("Session expiration checker started..."); Thread.sleep(SESSION_EXPIRATION_TIME); // Sleep for expiration time @@ -121,12 +120,13 @@ private static void sessionExpirationTask() { }).start(); } - public static void expirationTaskWake() //Wake up sleeping Expiration task thread - { - synchronized (sessionExpirationWait) - { - sessionExpirationWait.notify(); - } + /** + * allows sessionExpirationTask to run again, called when a login request is sent. + */ + public static void expirationTaskWake() { + synchronized (sessionExpirationWait) { + sessionExpirationWait.notify(); } + } } \ No newline at end of file diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java b/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java index 0abea56a1190..c719dd043085 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java @@ -42,6 +42,9 @@ public class LoginHandler implements HttpHandler { private Map sessions; private Map sessionCreationTimes; + /** + * Handles new login requests. + */ public LoginHandler(Map sessions, Map sessionCreationTimes) { this.sessions = sessions; this.sessionCreationTimes = sessionCreationTimes; diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/LogoutHandler.java b/server-session/src/main/java/com/iluwatar/sessionserver/LogoutHandler.java index 5bea06f2f866..fd58735a6790 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/LogoutHandler.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/LogoutHandler.java @@ -41,6 +41,9 @@ public class LogoutHandler implements HttpHandler { private Map sessions; private Map sessionCreationTimes; + /** + * Handles logging out requests. + */ public LogoutHandler(Map sessions, Map sessionCreationTimes) { this.sessions = sessions; this.sessionCreationTimes = sessionCreationTimes; diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index 6020891a3732..992b93653228 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -53,7 +53,7 @@ public void run() { if (isSuspended) { synchronized (lock) { try { - lock.wait(); //Wait until resumed :: busy loop fix + lock.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -69,20 +69,30 @@ public void run() { } } } - + /** + * suspend the thread. + */ public void suspendMe() { isSuspended = true; LOGGER.info("Begin to suspend BallThread"); } + /** + * notify run to resume. + */ + public void resumeMe() { isSuspended = false; LOGGER.info("Begin to resume BallThread"); + synchronized (lock) { lock.notify(); } } + /** + * Stop running thread. + */ public void stopMe() { this.isRunning = false; this.isSuspended = true; From 27325f21b4ffbad1c52bffb6bae6d37461e93bf7 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 30 Nov 2024 09:11:57 +0200 Subject: [PATCH 05/21] Checkstyle fix try 3 --- .../java/com/iluwatar/queue/load/leveling/MessageQueue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java index 6f7038682147..841a19d142a2 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java @@ -36,7 +36,7 @@ public class MessageQueue { private final BlockingQueue blkQueue; - public final Object serviceExecutorWait=new Object(); + public final Object serviceExecutorWait = new Object(); // Default constructor when called creates Blocking Queue object. public MessageQueue() { From 21090020a4b76a3b049d571873eb49fc7edaa377 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 30 Nov 2024 09:35:25 +0200 Subject: [PATCH 06/21] SonarQube fix try 1 --- .../java/com/iluwatar/queue/load/leveling/MessageQueue.java | 6 ++++-- .../src/main/java/com/iluwatar/sessionserver/App.java | 6 +++--- twin/src/main/java/com/iluwatar/twin/BallThread.java | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java index 841a19d142a2..1fbc5d18af1b 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java @@ -36,7 +36,7 @@ public class MessageQueue { private final BlockingQueue blkQueue; - public final Object serviceExecutorWait = new Object(); + public Object serviceExecutorWait =new Object(); // Default constructor when called creates Blocking Queue object. public MessageQueue() { @@ -51,7 +51,9 @@ public void submitMsg(Message msg) { try { if (null != msg) { blkQueue.add(msg); - serviceExecutorWait.notify(); + synchronized (serviceExecutorWait) { + serviceExecutorWait.notifyAll(); + } } } catch (Exception e) { LOGGER.error(e.getMessage()); diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/App.java b/server-session/src/main/java/com/iluwatar/sessionserver/App.java index 1311a573fe42..e6ca0c3b366e 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/App.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/App.java @@ -56,8 +56,8 @@ public class App { // Map to store session data (simulated using a HashMap) - private static Map sessions = new HashMap(); - private static Map sessionCreationTimes = new HashMap(); + private static Map sessions = new HashMap<>(); + private static Map sessionCreationTimes = new HashMap<>(); private static final long SESSION_EXPIRATION_TIME = 10000; private static final Object sessionExpirationWait = new Object(); // used to make expiration task wait or work based on event (login request sent or not) @@ -125,7 +125,7 @@ private static void sessionExpirationTask() { */ public static void expirationTaskWake() { synchronized (sessionExpirationWait) { - sessionExpirationWait.notify(); + sessionExpirationWait.notifyAll(); } } diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index 992b93653228..c449cef2df74 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -86,7 +86,7 @@ public void resumeMe() { LOGGER.info("Begin to resume BallThread"); synchronized (lock) { - lock.notify(); + lock.notifyAll(); } } @@ -97,7 +97,7 @@ public void stopMe() { this.isRunning = false; this.isSuspended = true; synchronized (lock) { - lock.notify(); + lock.notifyAll(); } } } From 992d899a96ce956db3892fb6b955232255bd4253 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 30 Nov 2024 10:04:24 +0200 Subject: [PATCH 07/21] SonarQube fix try 2 --- .../iluwatar/queue/load/leveling/MessageQueue.java | 5 ++--- .../iluwatar/queue/load/leveling/ServiceExecutor.java | 6 +++--- .../iluwatar/queue/load/leveling/TaskGenerator.java | 11 ++++++++--- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java index 1fbc5d18af1b..4b37d30106d7 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java @@ -36,7 +36,6 @@ public class MessageQueue { private final BlockingQueue blkQueue; - public Object serviceExecutorWait =new Object(); // Default constructor when called creates Blocking Queue object. public MessageQueue() { @@ -51,8 +50,8 @@ public void submitMsg(Message msg) { try { if (null != msg) { blkQueue.add(msg); - synchronized (serviceExecutorWait) { - serviceExecutorWait.notifyAll(); + synchronized (ServiceExecutor.serviceExecutorWait) { + ServiceExecutor.serviceExecutorWait.notifyAll(); } } } catch (Exception e) { diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java index 882ca0b211bf..9459fc2f2124 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java @@ -33,7 +33,7 @@ @Slf4j public class ServiceExecutor implements Runnable { private final MessageQueue msgQueue; - + public static final Object serviceExecutorWait = new Object(); public ServiceExecutor(MessageQueue msgQueue) { this.msgQueue = msgQueue; } @@ -50,8 +50,8 @@ public void run() { LOGGER.info(msg + " is served."); } else { LOGGER.info("Service Executor: Waiting for Messages to serve .. "); - synchronized (msgQueue.serviceExecutorWait) { - msgQueue.serviceExecutorWait.wait(); + synchronized (serviceExecutorWait) { + serviceExecutorWait.wait(); } } } diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/TaskGenerator.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/TaskGenerator.java index 9b1407277a27..e268ef1c5926 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/TaskGenerator.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/TaskGenerator.java @@ -50,7 +50,9 @@ public TaskGenerator(MessageQueue msgQueue, int msgCount) { */ public void submit(Message msg) { try { - this.msgQueue.submitMsg(msg); + synchronized (msg) { + this.msgQueue.submitMsg(msg); + } } catch (Exception e) { LOGGER.error(e.getMessage()); } @@ -66,9 +68,12 @@ public void run() { try { while (count > 0) { var statusMsg = "Message-" + count + " submitted by " + Thread.currentThread().getName(); - this.submit(new Message(statusMsg)); + Message msg = new Message(statusMsg); + synchronized (msg) { + this.submit(msg); + LOGGER.info(statusMsg); + } - LOGGER.info(statusMsg); // reduce the message count. count--; From ec0d423e8c8af4e1e777b1ea60764e5c9b95ea72 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 30 Nov 2024 10:34:27 +0200 Subject: [PATCH 08/21] SonarQube fix try 3 --- .../com/iluwatar/logaggregation/LogAggregator.java | 2 +- .../iluwatar/queue/load/leveling/TaskGenerator.java | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java b/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java index 06f9fcf3e879..634e8f27dbf7 100644 --- a/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java +++ b/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java @@ -130,7 +130,7 @@ private void startBufferFlusher() { */ public void bufferWake() { synchronized (bufferWait) { - bufferWait.notify(); + bufferWait.notifyAll(); } } } diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/TaskGenerator.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/TaskGenerator.java index e268ef1c5926..de280a518005 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/TaskGenerator.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/TaskGenerator.java @@ -50,9 +50,7 @@ public TaskGenerator(MessageQueue msgQueue, int msgCount) { */ public void submit(Message msg) { try { - synchronized (msg) { - this.msgQueue.submitMsg(msg); - } + this.msgQueue.submitMsg(msg); } catch (Exception e) { LOGGER.error(e.getMessage()); } @@ -68,12 +66,8 @@ public void run() { try { while (count > 0) { var statusMsg = "Message-" + count + " submitted by " + Thread.currentThread().getName(); - Message msg = new Message(statusMsg); - synchronized (msg) { - this.submit(msg); - LOGGER.info(statusMsg); - } - + LOGGER.info(statusMsg); + this.submit(new Message(statusMsg)); // reduce the message count. count--; From d84dc96f01766b38222ee8f7d4d37018640e6015 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Mon, 2 Dec 2024 00:20:39 +0200 Subject: [PATCH 09/21] SonarQube fix try 4 (Reliability and coverage in twin) --- .../java/com/iluwatar/twin/BallThread.java | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index c449cef2df74..92d522db0022 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -48,25 +48,20 @@ public class BallThread extends Thread { * Run the thread. */ public void run() { - - while (isRunning) { - if (isSuspended) { - synchronized (lock) { - try { + try { + while (isRunning) { + if (isSuspended) { + synchronized (lock) { lock.wait(); - } catch (InterruptedException e) { - throw new RuntimeException(e); } - } - } else { - twin.draw(); - twin.move(); - try { + } else { + twin.draw(); + twin.move(); Thread.sleep(250); - } catch (InterruptedException e) { - throw new RuntimeException(e); } } + } catch (InterruptedException e) { + throw new RuntimeException(e); } } /** From fca409f784095500b65183ac3776c8ec38042642 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Thu, 5 Dec 2024 14:10:12 +0200 Subject: [PATCH 10/21] Add Expiration Task state test (should be waiting at the start) --- .../java/com/iluwatar/sessionserver/App.java | 12 ++- .../com.iluwatar.sessionserver/AppTest.java | 77 +++++++++++++++++++ 2 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/App.java b/server-session/src/main/java/com/iluwatar/sessionserver/App.java index e6ca0c3b366e..8cd068cee3ef 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/App.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/App.java @@ -59,7 +59,8 @@ public class App { private static Map sessions = new HashMap<>(); private static Map sessionCreationTimes = new HashMap<>(); private static final long SESSION_EXPIRATION_TIME = 10000; - private static final Object sessionExpirationWait = new Object(); // used to make expiration task wait or work based on event (login request sent or not) + private static Object sessionExpirationWait = new Object(); // used to make expiration task wait or work based on event (login request sent or not) + private static Thread sessionExpirationThread; /** * Main entry point. @@ -84,7 +85,7 @@ public static void main(String[] args) throws IOException { } private static void sessionExpirationTask() { - new Thread(() -> { + sessionExpirationThread= new Thread(() -> { while (true) { try { synchronized (sessions) { @@ -117,7 +118,8 @@ private static void sessionExpirationTask() { Thread.currentThread().interrupt(); } } - }).start(); + }); + sessionExpirationThread.start(); } /** @@ -129,4 +131,8 @@ public static void expirationTaskWake() { } } + public static Thread.State getExpirationTaskState(){ + return sessionExpirationThread.getState(); + } + } \ No newline at end of file diff --git a/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java b/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java new file mode 100644 index 000000000000..a4868e552385 --- /dev/null +++ b/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java @@ -0,0 +1,77 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.sessionserver; + +import static java.lang.Thread.State; +import static java.lang.Thread.State.WAITING; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpExchange; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * LoginHandlerTest. + */ +@Slf4j +public class AppTest { + + private LoginHandler loginHandler; + //private Headers headers; + private Map sessions; + private Map sessionCreationTimes; + + @Mock + private HttpExchange exchange; + + /** + * Setup tests. + */ + @BeforeEach + public void setUp() throws IOException { + MockitoAnnotations.initMocks(this); + App.main(new String [] {}); + } + + @Test + public void expirationTaskStartStateTest() { + + //assert + LOGGER.info("Expiration Task Status: "+String.valueOf(App.getExpirationTaskState())); + assertEquals(App.getExpirationTaskState(),WAITING); + + } + +} \ No newline at end of file From 986130af3a9dc22047527e1791af179b8d258def Mon Sep 17 00:00:00 2001 From: alhusseain Date: Thu, 5 Dec 2024 14:27:32 +0200 Subject: [PATCH 11/21] Add Expiration Task state test (should be waiting at the start) --- .../java/com/iluwatar/sessionserver/App.java | 4 ++-- .../com.iluwatar.sessionserver/AppTest.java | 17 ----------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/App.java b/server-session/src/main/java/com/iluwatar/sessionserver/App.java index 8cd068cee3ef..f38186344f23 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/App.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/App.java @@ -85,7 +85,7 @@ public static void main(String[] args) throws IOException { } private static void sessionExpirationTask() { - sessionExpirationThread= new Thread(() -> { + sessionExpirationThread = new Thread(() -> { while (true) { try { synchronized (sessions) { @@ -131,7 +131,7 @@ public static void expirationTaskWake() { } } - public static Thread.State getExpirationTaskState(){ + public static Thread.State getExpirationTaskState() { return sessionExpirationThread.getState(); } diff --git a/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java b/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java index a4868e552385..b8efb28b8216 100644 --- a/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java +++ b/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java @@ -24,22 +24,13 @@ */ package com.iluwatar.sessionserver; -import static java.lang.Thread.State; import static java.lang.Thread.State.WAITING; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.when; -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpExchange; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** @@ -48,14 +39,6 @@ @Slf4j public class AppTest { - private LoginHandler loginHandler; - //private Headers headers; - private Map sessions; - private Map sessionCreationTimes; - - @Mock - private HttpExchange exchange; - /** * Setup tests. */ From e31b12b38e01776b46f041421a5f97ef78d17a1a Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 7 Dec 2024 14:00:18 +0200 Subject: [PATCH 12/21] Add Expiration Task state test (should be sleeping after it is woken) --- .../com.iluwatar.sessionserver/AppTest.java | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java b/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java index b8efb28b8216..a3c46afbc8f0 100644 --- a/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java +++ b/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java @@ -24,37 +24,74 @@ */ package com.iluwatar.sessionserver; +import static java.lang.Thread.State.TIMED_WAITING; import static java.lang.Thread.State.WAITING; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; import java.io.IOException; import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.mockito.MockitoAnnotations; /** * LoginHandlerTest. */ @Slf4j +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class AppTest { + /** + * Start App before tests + * @throws IOException + */ + + @BeforeAll + public static void init() throws IOException { + App.main(new String [] {}); + } + /** * Setup tests. */ + @BeforeEach - public void setUp() throws IOException { + public void setUp() { MockitoAnnotations.initMocks(this); - App.main(new String [] {}); } + /** + * Run the Start state test first + * Checks that the session expiration task is waiting when the app is first started + */ + @Test + @Order(1) public void expirationTaskStartStateTest() { - //assert LOGGER.info("Expiration Task Status: "+String.valueOf(App.getExpirationTaskState())); assertEquals(App.getExpirationTaskState(),WAITING); + } + + /** + * Run the wake state test second + * Test whether expiration Task is currently sleeping or not (should sleep when woken) + */ + + @Test + @Order(2) + public void expirationTaskWakeStateTest() throws InterruptedException { + App.expirationTaskWake(); + Thread.sleep(200); // Wait until sessionExpirationTask is sleeping + LOGGER.info("Expiration Task Status: "+String.valueOf(App.getExpirationTaskState())); + assertEquals(App.getExpirationTaskState(),TIMED_WAITING); } } \ No newline at end of file From 7be96ca463aade0609b09e7b2dd133ba17bc9d82 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 7 Dec 2024 14:02:06 +0200 Subject: [PATCH 13/21] Add Expiration Task state test (should be sleeping after it is woken) --- .../src/test/java/com.iluwatar.sessionserver/AppTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java b/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java index a3c46afbc8f0..d22e202e60f4 100644 --- a/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java +++ b/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java @@ -27,11 +27,8 @@ import static java.lang.Thread.State.TIMED_WAITING; import static java.lang.Thread.State.WAITING; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.when; - import java.io.IOException; import lombok.extern.slf4j.Slf4j; -import org.junit.Before; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.MethodOrderer; From 30d6eb3a343146920dbf8aac7134b97927e4fb6b Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 7 Dec 2024 15:07:47 +0200 Subject: [PATCH 14/21] Add BallThread resume and suspend state test --- .../test/java/com/iluwatar/twin/BallThreadTest.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java b/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java index 26cf78509dcf..244d11a6dfaa 100644 --- a/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java +++ b/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java @@ -27,6 +27,7 @@ import static java.lang.Thread.UncaughtExceptionHandler; import static java.lang.Thread.sleep; import static java.time.Duration.ofMillis; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTimeout; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -35,12 +36,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; /** * BallThreadTest * */ +@Slf4j class BallThreadTest { /** @@ -59,12 +62,12 @@ void testSuspend() { verify(ballItem, atLeastOnce()).draw(); verify(ballItem, atLeastOnce()).move(); ballThread.suspendMe(); - sleep(1000); + LOGGER.info("Current ballThread State: "+ballThread.getState()); + assertEquals(ballThread.getState(), Thread.State.WAITING); ballThread.stopMe(); ballThread.join(); - verifyNoMoreInteractions(ballItem); }); } @@ -86,8 +89,9 @@ void testResume() { sleep(1000); verifyNoMoreInteractions(ballItem); - ballThread.resumeMe(); + LOGGER.info("Current ballThread State: "+ballThread.getState()); + assertEquals(ballThread.getState(), Thread.State.RUNNABLE); sleep(300); verify(ballItem, atLeastOnce()).draw(); verify(ballItem, atLeastOnce()).move(); From d0e0d87debde022f0ed2f83c744098614cd228e1 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 7 Dec 2024 19:31:27 +0200 Subject: [PATCH 15/21] Add Service Executor Start and Wake State Test --- .../queue/load/leveling/MessageQueue.java | 5 +-- .../queue/load/leveling/ServiceExecutor.java | 5 ++- .../load/leveling/TaskGenSrvExeTest.java | 35 +++++++++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java index 4b37d30106d7..c42723703882 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/MessageQueue.java @@ -36,6 +36,7 @@ public class MessageQueue { private final BlockingQueue blkQueue; + public final Object serviceExecutorWait = new Object(); // Default constructor when called creates Blocking Queue object. public MessageQueue() { @@ -50,8 +51,8 @@ public void submitMsg(Message msg) { try { if (null != msg) { blkQueue.add(msg); - synchronized (ServiceExecutor.serviceExecutorWait) { - ServiceExecutor.serviceExecutorWait.notifyAll(); + synchronized (serviceExecutorWait) { + serviceExecutorWait.notifyAll(); } } } catch (Exception e) { diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java index 9459fc2f2124..92b5379d6eea 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/ServiceExecutor.java @@ -33,7 +33,6 @@ @Slf4j public class ServiceExecutor implements Runnable { private final MessageQueue msgQueue; - public static final Object serviceExecutorWait = new Object(); public ServiceExecutor(MessageQueue msgQueue) { this.msgQueue = msgQueue; } @@ -50,8 +49,8 @@ public void run() { LOGGER.info(msg + " is served."); } else { LOGGER.info("Service Executor: Waiting for Messages to serve .. "); - synchronized (serviceExecutorWait) { - serviceExecutorWait.wait(); + synchronized (msgQueue.serviceExecutorWait) { + msgQueue.serviceExecutorWait.wait(); } } } diff --git a/queue-based-load-leveling/src/test/java/com/iluwatar/queue/load/leveling/TaskGenSrvExeTest.java b/queue-based-load-leveling/src/test/java/com/iluwatar/queue/load/leveling/TaskGenSrvExeTest.java index 0a03bc560a1d..b51766b69bee 100644 --- a/queue-based-load-leveling/src/test/java/com/iluwatar/queue/load/leveling/TaskGenSrvExeTest.java +++ b/queue-based-load-leveling/src/test/java/com/iluwatar/queue/load/leveling/TaskGenSrvExeTest.java @@ -24,14 +24,17 @@ */ package com.iluwatar.queue.load.leveling; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; /** * Test case for submitting Message to Blocking Queue by TaskGenerator and retrieve the message by * ServiceExecutor. */ +@Slf4j class TaskGenSrvExeTest { @Test @@ -53,4 +56,36 @@ void taskGeneratorTest() { assertNotNull(srvExeThr); } + /** + * Tests that service executor waits at start since no message is sent to execute upon. + * @throws InterruptedException + */ + @Test + void serviceExecutorStartStateTest() throws InterruptedException { + var msgQueue = new MessageQueue(); + var srvRunnable = new ServiceExecutor(msgQueue); + var srvExeThr = new Thread(srvRunnable); + srvExeThr.start(); + Thread.sleep(200); // sleep a little until service executor thread waits + LOGGER.info("Current Service Executor State: " + srvExeThr.getState()); + assertEquals(srvExeThr.getState(), Thread.State.WAITING); + + } + + @Test + void serviceExecutorWakeStateTest() throws InterruptedException { + var msgQueue = new MessageQueue(); + var srvRunnable = new ServiceExecutor(msgQueue); + var srvExeThr = new Thread(srvRunnable); + srvExeThr.start(); + Thread.sleep(200); // sleep a little until service executor thread waits + synchronized (msgQueue.serviceExecutorWait){ + msgQueue.serviceExecutorWait.notifyAll(); + } + var srvExeState = srvExeThr.getState(); + LOGGER.info("Current Service Executor State: " + srvExeState); + assertEquals(srvExeState, Thread.State.RUNNABLE); + + } + } From 4751cb89ea504719d584eac02afe2dabd46135d0 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 7 Dec 2024 21:02:14 +0200 Subject: [PATCH 16/21] Add Service Executor Start and Wake State Test --- .../com/iluwatar/queue/load/leveling/TaskGenSrvExeTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/queue-based-load-leveling/src/test/java/com/iluwatar/queue/load/leveling/TaskGenSrvExeTest.java b/queue-based-load-leveling/src/test/java/com/iluwatar/queue/load/leveling/TaskGenSrvExeTest.java index b51766b69bee..2407ca2116a1 100644 --- a/queue-based-load-leveling/src/test/java/com/iluwatar/queue/load/leveling/TaskGenSrvExeTest.java +++ b/queue-based-load-leveling/src/test/java/com/iluwatar/queue/load/leveling/TaskGenSrvExeTest.java @@ -24,8 +24,10 @@ */ package com.iluwatar.queue.load.leveling; +import static java.util.concurrent.CompletableFuture.anyOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; @@ -84,7 +86,8 @@ void serviceExecutorWakeStateTest() throws InterruptedException { } var srvExeState = srvExeThr.getState(); LOGGER.info("Current Service Executor State: " + srvExeState); - assertEquals(srvExeState, Thread.State.RUNNABLE); + // assert that state changes from waiting + assertTrue(srvExeState != Thread.State.WAITING); } From 7afa9c8b21ec687d8267dc8f848deed73f842c64 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Sat, 7 Dec 2024 22:04:34 +0200 Subject: [PATCH 17/21] LogoutHandlerTest correct --- .../test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java b/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java index 05ce10c57813..2bf216819b8c 100644 --- a/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java +++ b/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java @@ -73,7 +73,6 @@ public void testHandler_SessionNotExpired() { sessionCreationTimes.put("1234", Instant.now()); //Fake login details since LoginHandler isn't called - App.expirationTaskWake(); when(exchange.getRequestHeaders()).thenReturn(headers); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); when(exchange.getResponseBody()).thenReturn(outputStream); From b5073d6ad1b10f9c569b085d638139c4df08398f Mon Sep 17 00:00:00 2001 From: alhusseain Date: Wed, 25 Dec 2024 12:40:28 +0200 Subject: [PATCH 18/21] LogoutHandlerTest correct --- .../java/com.iluwatar.sessionserver/LogoutHandlerTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java b/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java index 2bf216819b8c..1b9d817b0114 100644 --- a/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java +++ b/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java @@ -69,8 +69,7 @@ public void setUp() { public void testHandler_SessionNotExpired() { //assemble - sessions.put("1234", 1);//Fake login details since LoginHandler isn't called - + sessions.put("1234", 1); //Fake login details since LoginHandler isn't called sessionCreationTimes.put("1234", Instant.now()); //Fake login details since LoginHandler isn't called when(exchange.getRequestHeaders()).thenReturn(headers); From 1c045483cc690779a53e00cec687472937d90ff7 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Tue, 18 Feb 2025 19:40:51 +0200 Subject: [PATCH 19/21] implemented review changes --- .../logaggregation/LogAggregator.java | 9 +- .../logaggregation/LogAggregatorTest.java | 3 + .../com/iluwatar/queue/load/leveling/App.java | 4 +- .../java/com/iluwatar/sessionserver/App.java | 67 ++++--------- .../iluwatar/sessionserver/LoginHandler.java | 1 - .../com.iluwatar.sessionserver/AppTest.java | 94 ------------------- .../java/com/iluwatar/twin/BallThread.java | 1 - 7 files changed, 31 insertions(+), 148 deletions(-) delete mode 100644 server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java diff --git a/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java b/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java index 634e8f27dbf7..7b413e2d3168 100644 --- a/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java +++ b/microservices-log-aggregation/src/main/java/com/iluwatar/logaggregation/LogAggregator.java @@ -27,6 +27,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import lombok.extern.slf4j.Slf4j; @@ -108,7 +109,8 @@ private void flushBuffer() { } private void startBufferFlusher() { - executorService.execute(() -> { + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + scheduler.scheduleAtFixedRate(() -> { while (!Thread.currentThread().isInterrupted()) { try { synchronized (bufferWait) { @@ -116,13 +118,12 @@ private void startBufferFlusher() { bufferWait.wait(); } } - Thread.sleep(5000); // Flush every 5 seconds. flushBuffer(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } - }); + }, 5, 5, TimeUnit.SECONDS); } /** @@ -133,4 +134,4 @@ public void bufferWake() { bufferWait.notifyAll(); } } -} +} \ No newline at end of file diff --git a/microservices-log-aggregation/src/test/java/com/iluwatar/logaggregation/LogAggregatorTest.java b/microservices-log-aggregation/src/test/java/com/iluwatar/logaggregation/LogAggregatorTest.java index 6c385f35c6c3..52832df79757 100644 --- a/microservices-log-aggregation/src/test/java/com/iluwatar/logaggregation/LogAggregatorTest.java +++ b/microservices-log-aggregation/src/test/java/com/iluwatar/logaggregation/LogAggregatorTest.java @@ -24,13 +24,16 @@ */ package com.iluwatar.logaggregation; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.time.LocalDateTime; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; diff --git a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/App.java b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/App.java index 7042ff7b79a2..587d1b28733b 100644 --- a/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/App.java +++ b/queue-based-load-leveling/src/main/java/com/iluwatar/queue/load/leveling/App.java @@ -26,6 +26,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; @@ -88,7 +89,8 @@ public static void main(String[] args) { // Create e service which should process the submitted jobs. final var srvRunnable = new ServiceExecutor(msgQueue); - + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + scheduler.scheduleAtFixedRate(srvRunnable, 1, 1, TimeUnit.SECONDS); // Create a ThreadPool of 2 threads and // submit all Runnable task for execution to executor executor = Executors.newFixedThreadPool(2); diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/App.java b/server-session/src/main/java/com/iluwatar/sessionserver/App.java index f38186344f23..30d3bb88eac2 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/App.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/App.java @@ -31,6 +31,9 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; @@ -59,8 +62,7 @@ public class App { private static Map sessions = new HashMap<>(); private static Map sessionCreationTimes = new HashMap<>(); private static final long SESSION_EXPIRATION_TIME = 10000; - private static Object sessionExpirationWait = new Object(); // used to make expiration task wait or work based on event (login request sent or not) - private static Thread sessionExpirationThread; + /** * Main entry point. @@ -79,60 +81,31 @@ public static void main(String[] args) throws IOException { server.start(); // Start background task to check for expired sessions - sessionExpirationTask(); LOGGER.info("Server started. Listening on port 8080..."); + + ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + scheduler.scheduleAtFixedRate(App::sessionExpirationTask, SESSION_EXPIRATION_TIME, SESSION_EXPIRATION_TIME, TimeUnit.MILLISECONDS); } private static void sessionExpirationTask() { - sessionExpirationThread = new Thread(() -> { - while (true) { - try { - synchronized (sessions) { - if (sessions.isEmpty()) { - synchronized (sessionExpirationWait) { - sessionExpirationWait.wait(); - } - } - } - LOGGER.info("Session expiration checker started..."); - Thread.sleep(SESSION_EXPIRATION_TIME); // Sleep for expiration time - Instant currentTime = Instant.now(); - synchronized (sessions) { - synchronized (sessionCreationTimes) { - Iterator> iterator = - sessionCreationTimes.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - if (entry.getValue().plusMillis(SESSION_EXPIRATION_TIME).isBefore(currentTime)) { - LOGGER.info("User " + entry.getValue() + " removed"); - sessions.remove(entry.getKey()); - iterator.remove(); - } - } - } + LOGGER.info("Session expiration checker started..."); + Instant currentTime = Instant.now(); + synchronized (sessions) { + synchronized (sessionCreationTimes) { + Iterator> iterator = + sessionCreationTimes.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + if (entry.getValue().plusMillis(SESSION_EXPIRATION_TIME).isBefore(currentTime)) { + LOGGER.info("User " + entry.getValue() + " removed"); + sessions.remove(entry.getKey()); + iterator.remove(); } - LOGGER.info("Session expiration checker finished!"); - } catch (InterruptedException e) { - LOGGER.error("An error occurred: ", e); - Thread.currentThread().interrupt(); } } - }); - sessionExpirationThread.start(); - } - - /** - * allows sessionExpirationTask to run again, called when a login request is sent. - */ - public static void expirationTaskWake() { - synchronized (sessionExpirationWait) { - sessionExpirationWait.notifyAll(); } - } - - public static Thread.State getExpirationTaskState() { - return sessionExpirationThread.getState(); + LOGGER.info("Session expiration checker finished!"); } } \ No newline at end of file diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java b/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java index c719dd043085..d49a66a54b91 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java @@ -63,7 +63,6 @@ public void handle(HttpExchange exchange) { // Set session ID as cookie exchange.getResponseHeaders().add("Set-Cookie", "sessionID=" + sessionId); - App.expirationTaskWake(); // Wake up expiration task // Send response String response = "Login successful!\n" + "Session ID: " + sessionId; try { diff --git a/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java b/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java deleted file mode 100644 index d22e202e60f4..000000000000 --- a/server-session/src/test/java/com.iluwatar.sessionserver/AppTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). - * - * The MIT License - * Copyright © 2014-2022 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.sessionserver; - -import static java.lang.Thread.State.TIMED_WAITING; -import static java.lang.Thread.State.WAITING; -import static org.junit.jupiter.api.Assertions.assertEquals; -import java.io.IOException; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.mockito.MockitoAnnotations; - -/** - * LoginHandlerTest. - */ -@Slf4j -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class AppTest { - - /** - * Start App before tests - * @throws IOException - */ - - @BeforeAll - public static void init() throws IOException { - App.main(new String [] {}); - } - - /** - * Setup tests. - */ - - @BeforeEach - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - /** - * Run the Start state test first - * Checks that the session expiration task is waiting when the app is first started - */ - - @Test - @Order(1) - public void expirationTaskStartStateTest() { - //assert - LOGGER.info("Expiration Task Status: "+String.valueOf(App.getExpirationTaskState())); - assertEquals(App.getExpirationTaskState(),WAITING); - } - - - /** - * Run the wake state test second - * Test whether expiration Task is currently sleeping or not (should sleep when woken) - */ - - @Test - @Order(2) - public void expirationTaskWakeStateTest() throws InterruptedException { - App.expirationTaskWake(); - Thread.sleep(200); // Wait until sessionExpirationTask is sleeping - LOGGER.info("Expiration Task Status: "+String.valueOf(App.getExpirationTaskState())); - assertEquals(App.getExpirationTaskState(),TIMED_WAITING); - } - -} \ No newline at end of file diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index 92d522db0022..c661c3d0fc06 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -57,7 +57,6 @@ public void run() { } else { twin.draw(); twin.move(); - Thread.sleep(250); } } } catch (InterruptedException e) { From dbf6a7b59f45a111297cad290ddce1aee20746b1 Mon Sep 17 00:00:00 2001 From: alhusseain Date: Tue, 18 Feb 2025 20:34:31 +0200 Subject: [PATCH 20/21] implemented review changes --- .../java/com/iluwatar/twin/BallThread.java | 18 +++++++++--------- .../java/com/iluwatar/twin/BallThreadTest.java | 10 ++++------ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index c661c3d0fc06..dfca68daca1e 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -48,19 +48,19 @@ public class BallThread extends Thread { * Run the thread. */ public void run() { - try { - while (isRunning) { - if (isSuspended) { - synchronized (lock) { + while (isRunning) { + if (!isSuspended) { + twin.draw(); + twin.move(); + } else { + synchronized (lock) { + try { lock.wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); } - } else { - twin.draw(); - twin.move(); } } - } catch (InterruptedException e) { - throw new RuntimeException(e); } } /** diff --git a/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java b/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java index 244d11a6dfaa..ad6afec114b7 100644 --- a/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java +++ b/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java @@ -28,6 +28,7 @@ import static java.lang.Thread.sleep; import static java.time.Duration.ofMillis; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTimeout; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -68,7 +69,6 @@ void testSuspend() { ballThread.stopMe(); ballThread.join(); - verifyNoMoreInteractions(ballItem); }); } @@ -90,16 +90,15 @@ void testResume() { verifyNoMoreInteractions(ballItem); ballThread.resumeMe(); + sleep(250); LOGGER.info("Current ballThread State: "+ballThread.getState()); - assertEquals(ballThread.getState(), Thread.State.RUNNABLE); - sleep(300); + assertNotSame(ballThread.getState(), Thread.State.WAITING); verify(ballItem, atLeastOnce()).draw(); verify(ballItem, atLeastOnce()).move(); ballThread.stopMe(); ballThread.join(); - verifyNoMoreInteractions(ballItem); }); } @@ -114,11 +113,10 @@ void testInterrupt() { ballThread.setUncaughtExceptionHandler(exceptionHandler); ballThread.setTwin(mock(BallItem.class)); ballThread.start(); + ballThread.suspendMe(); ballThread.interrupt(); ballThread.join(); - verify(exceptionHandler).uncaughtException(eq(ballThread), any(RuntimeException.class)); - verifyNoMoreInteractions(exceptionHandler); }); } } \ No newline at end of file From 84072459eb89418394eedd1c3a4759e14da0baaa Mon Sep 17 00:00:00 2001 From: alhusseain Date: Tue, 18 Feb 2025 21:31:09 +0200 Subject: [PATCH 21/21] implemented review changes --- .../java/com/iluwatar/sessionserver/App.java | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/server-session/src/main/java/com/iluwatar/sessionserver/App.java b/server-session/src/main/java/com/iluwatar/sessionserver/App.java index 30d3bb88eac2..26dad15a8bc7 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/App.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/App.java @@ -91,21 +91,24 @@ public static void main(String[] args) throws IOException { private static void sessionExpirationTask() { LOGGER.info("Session expiration checker started..."); Instant currentTime = Instant.now(); - synchronized (sessions) { - synchronized (sessionCreationTimes) { - Iterator> iterator = - sessionCreationTimes.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - if (entry.getValue().plusMillis(SESSION_EXPIRATION_TIME).isBefore(currentTime)) { - LOGGER.info("User " + entry.getValue() + " removed"); - sessions.remove(entry.getKey()); - iterator.remove(); + try { + synchronized (sessions) { + synchronized (sessionCreationTimes) { + Iterator> iterator = + sessionCreationTimes.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + if (entry.getValue().plusMillis(SESSION_EXPIRATION_TIME).isBefore(currentTime)) { + LOGGER.info("User " + entry.getValue() + " removed"); + sessions.remove(entry.getKey()); + iterator.remove(); + } } } } + } catch (Exception e) { + LOGGER.error("An error occurred: ", e); } LOGGER.info("Session expiration checker finished!"); } - } \ No newline at end of file