diff --git a/double-buffer/README.md b/double-buffer/README.md index 04b47ad97f7e..bf6b3877b5cd 100644 --- a/double-buffer/README.md +++ b/double-buffer/README.md @@ -1,38 +1,31 @@ --- -title: Double Buffer +title: Double Buffer category: Behavioral language: en -tag: - - Performance - - Game programming +tag: + - Buffering + - Game programming + - Optimization + - Performance --- - -## Intent -Double buffering is a term used to describe a device that has two buffers. The usage of multiple -buffers increases the overall throughput of a device and helps prevents bottlenecks. This example -shows using double buffer pattern on graphics. It is used to show one image or frame while a separate -frame is being buffered to be shown next. This method makes animations and games look more realistic -than the same done in a single buffer mode. + +## Intent + +The Double Buffer pattern aims to reduce the time necessary for rendering and displaying graphical or computational data by utilizing two buffers. One buffer is used for rendering the next frame or computing the next set of data, while the other is used to display the current frame or data set to the user. ## Explanation Real world example -> A typical example, and one that every game engine must address, is rendering. When the game draws -> the world the users see, it does so one piece at a time -- the mountains in the distance, -> the rolling hills, the trees, each in its turn. If the user watched the view draw incrementally -> like that, the illusion of a coherent world would be shattered. The scene must update smoothly -> and quickly, displaying a series of complete frames, each appearing instantly. Double buffering solves -> the problem. + +> A typical example, and one that every game engine must address, is rendering. When the game draws the world the users see, it does so one piece at a time -- the mountains in the distance, the rolling hills, the trees, each in its turn. If the user watched the view draw incrementally like that, the illusion of a coherent world would be shattered. The scene must update smoothly and quickly, displaying a series of complete frames, each appearing instantly. Double buffering solves the problem. In plain words -> It ensures a state that is being rendered correctly while that state is modifying incrementally. It is -> widely used in computer graphics. + +> It ensures a state that is being rendered correctly while that state is modifying incrementally. It is widely used in computer graphics. Wikipedia says -> In computer science, multiple buffering is the use of more than one buffer to hold a block of data, -> so that a "reader" will see a complete (though perhaps old) version of the data, rather than a -> partially updated version of the data being created by a "writer". It is very commonly used for -> computer display images. + +> In computer science, multiple buffering is the use of more than one buffer to hold a block of data, so that a "reader" will see a complete (though perhaps old) version of the data, rather than a partially updated version of the data being created by a "writer". It is very commonly used for computer display images. **Programmatic Example** @@ -44,76 +37,77 @@ Buffer interface that assures basic functionalities of a buffer. */ public interface Buffer { - /** - * Clear the pixel in (x, y). - * - * @param x X coordinate - * @param y Y coordinate - */ - void clear(int x, int y); - - /** - * Draw the pixel in (x, y). - * - * @param x X coordinate - * @param y Y coordinate - */ - void draw(int x, int y); - - /** - * Clear all the pixels. - */ - void clearAll(); - - /** - * Get all the pixels. - * - * @return pixel list - */ - Pixel[] getPixels(); + /** + * Clear the pixel in (x, y). + * + * @param x X coordinate + * @param y Y coordinate + */ + void clear(int x, int y); + + /** + * Draw the pixel in (x, y). + * + * @param x X coordinate + * @param y Y coordinate + */ + void draw(int x, int y); + + /** + * Clear all the pixels. + */ + void clearAll(); + + /** + * Get all the pixels. + * + * @return pixel list + */ + Pixel[] getPixels(); } ``` One of the implementation of Buffer interface. + ```java /** * FrameBuffer implementation class. */ public class FrameBuffer implements Buffer { - public static final int WIDTH = 10; - public static final int HEIGHT = 8; + public static final int WIDTH = 10; + public static final int HEIGHT = 8; - private final Pixel[] pixels = new Pixel[WIDTH * HEIGHT]; + private final Pixel[] pixels = new Pixel[WIDTH * HEIGHT]; - public FrameBuffer() { - clearAll(); - } + public FrameBuffer() { + clearAll(); + } - @Override - public void clear(int x, int y) { - pixels[getIndex(x, y)] = Pixel.WHITE; - } + @Override + public void clear(int x, int y) { + pixels[getIndex(x, y)] = Pixel.WHITE; + } - @Override - public void draw(int x, int y) { - pixels[getIndex(x, y)] = Pixel.BLACK; - } + @Override + public void draw(int x, int y) { + pixels[getIndex(x, y)] = Pixel.BLACK; + } - @Override - public void clearAll() { - Arrays.fill(pixels, Pixel.WHITE); - } + @Override + public void clearAll() { + Arrays.fill(pixels, Pixel.WHITE); + } - @Override - public Pixel[] getPixels() { - return pixels; - } + @Override + public Pixel[] getPixels() { + return pixels; + } - private int getIndex(int x, int y) { - return x + WIDTH * y; - } + private int getIndex(int x, int y) { + return x + WIDTH * y; + } } ``` @@ -123,11 +117,13 @@ public class FrameBuffer implements Buffer { */ public enum Pixel { - WHITE, - BLACK; + WHITE, + BLACK; } ``` + Scene represents the game scene where current buffer has already been rendered. + ```java /** * Scene class. Render the output frame. @@ -135,93 +131,94 @@ Scene represents the game scene where current buffer has already been rendered. @Slf4j public class Scene { - private final Buffer[] frameBuffers; - - private int current; - - private int next; - - /** - * Constructor of Scene. - */ - public Scene() { - frameBuffers = new FrameBuffer[2]; - frameBuffers[0] = new FrameBuffer(); - frameBuffers[1] = new FrameBuffer(); - current = 0; - next = 1; - } - - /** - * Draw the next frame. - * - * @param coordinateList list of pixels of which the color should be black - */ - public void draw(List> coordinateList) { - LOGGER.info("Start drawing next frame"); - LOGGER.info("Current buffer: " + current + " Next buffer: " + next); - frameBuffers[next].clearAll(); - coordinateList.forEach(coordinate -> { - var x = coordinate.getKey(); - var y = coordinate.getValue(); - frameBuffers[next].draw(x, y); - }); - LOGGER.info("Swap current and next buffer"); - swap(); - LOGGER.info("Finish swapping"); - LOGGER.info("Current buffer: " + current + " Next buffer: " + next); - } - - public Buffer getBuffer() { - LOGGER.info("Get current buffer: " + current); - return frameBuffers[current]; - } - - private void swap() { - current = current ^ next; - next = current ^ next; - current = current ^ next; - } + private final Buffer[] frameBuffers; + + private int current; + + private int next; + + /** + * Constructor of Scene. + */ + public Scene() { + frameBuffers = new FrameBuffer[2]; + frameBuffers[0] = new FrameBuffer(); + frameBuffers[1] = new FrameBuffer(); + current = 0; + next = 1; + } + + /** + * Draw the next frame. + * + * @param coordinateList list of pixels of which the color should be black + */ + public void draw(List> coordinateList) { + LOGGER.info("Start drawing next frame"); + LOGGER.info("Current buffer: " + current + " Next buffer: " + next); + frameBuffers[next].clearAll(); + coordinateList.forEach(coordinate -> { + var x = coordinate.getKey(); + var y = coordinate.getValue(); + frameBuffers[next].draw(x, y); + }); + LOGGER.info("Swap current and next buffer"); + swap(); + LOGGER.info("Finish swapping"); + LOGGER.info("Current buffer: " + current + " Next buffer: " + next); + } + + public Buffer getBuffer() { + LOGGER.info("Get current buffer: " + current); + return frameBuffers[current]; + } + + private void swap() { + current = current ^ next; + next = current ^ next; + current = current ^ next; + } } ``` ```java -public static void main(String[] args) { - final var scene = new Scene(); - var drawPixels1 = List.of( - new MutablePair<>(1, 1), - new MutablePair<>(5, 6), - new MutablePair<>(3, 2) - ); - scene.draw(drawPixels1); - var buffer1 = scene.getBuffer(); - printBlackPixelCoordinate(buffer1); - - var drawPixels2 = List.of( - new MutablePair<>(3, 7), - new MutablePair<>(6, 1) - ); - scene.draw(drawPixels2); - var buffer2 = scene.getBuffer(); - printBlackPixelCoordinate(buffer2); - } - - private static void printBlackPixelCoordinate(Buffer buffer) { - StringBuilder log = new StringBuilder("Black Pixels: "); - var pixels = buffer.getPixels(); - for (var i = 0; i < pixels.length; ++i) { - if (pixels[i] == Pixel.BLACK) { - var y = i / FrameBuffer.WIDTH; - var x = i % FrameBuffer.WIDTH; +public static void main(String[]args){ +final var scene=new Scene(); + var drawPixels1=List.of( + new MutablePair<>(1,1), + new MutablePair<>(5,6), + new MutablePair<>(3,2) + ); + scene.draw(drawPixels1); + var buffer1=scene.getBuffer(); + printBlackPixelCoordinate(buffer1); + + var drawPixels2=List.of( + new MutablePair<>(3,7), + new MutablePair<>(6,1) + ); + scene.draw(drawPixels2); + var buffer2=scene.getBuffer(); + printBlackPixelCoordinate(buffer2); + } + +private static void printBlackPixelCoordinate(Buffer buffer){ + StringBuilder log=new StringBuilder("Black Pixels: "); + var pixels=buffer.getPixels(); + for(var i=0;idynamic-proxy gateway slob + server-session diff --git a/server-session/README.md b/server-session/README.md index 38ff1c298285..e0d50173835b 100644 --- a/server-session/README.md +++ b/server-session/README.md @@ -3,9 +3,9 @@ title: Server Session category: Behavioral language: en tag: - - Session Management - - Session Tracking - Cookies + - Session management + - State tracking --- ## Also known as @@ -20,7 +20,7 @@ Within the context of a client-server relationship, the server is responsible fo Real-world example -> Consider a gaming website which stores user profile data such as username, password, highscore, hours played, etc. Since this website is accessed over the internet which uses the HTTP protocol, all requests sent to the server are stateless. In order for the page to display user relevent information without re-authenticating the user on every request a session must be created. Once the session is created the user can access the homescreen, statistics page, setting page, etc. and view profile specific data without needing to login in on every page request. +> Consider a gaming website which stores user profile data such as username, password, high-score, hours played, etc. Since this website is accessed over the internet which uses the HTTP protocol, all requests sent to the server are stateless. In order for the page to display user relevant information without re-authenticating the user on every request a session must be created. Once the session is created the user can access the homescreen, statistics page, setting page, etc. and view profile specific data without needing to log in on every page request. In plain words @@ -179,13 +179,13 @@ Sessions are often given a maximum time in which they will be maintained. The se ## Class diagram -![alt text](./etc/adapter.urm.png "Adapter class diagram") +![Server Session class diagram](./etc/server-session.urm.puml "Server Session class diagram") ## Applicability Use the Adapter pattern when -* When a user logs into a website or web application and you want to keep track of their authentication status. +* When a user logs into a website or web application, and you want to keep track of their authentication status. * In e-commerce websites when you want to maintain the contents of a user's shopping cart across different pages and visits. * When you want to store user preferences and settings, such as language preferences, theme choices, or any other customizable options. * When you want to keep track of user activity and behavior on a website for the sake of analytics purposes. diff --git a/server-session/etc/server-session.urm.puml b/server-session/etc/server-session.urm.puml new file mode 100644 index 000000000000..a33aa720a274 --- /dev/null +++ b/server-session/etc/server-session.urm.puml @@ -0,0 +1,27 @@ +@startuml +package com.iluwatar.sessionserver { + class App { + - LOGGER : Logger {static} + - SESSION_EXPIRATION_TIME : long {static} + - sessionCreationTimes : Map {static} + - sessions : Map {static} + + App() + + main(args : String[]) {static} + - sessionExpirationTask() {static} + } + class LoginHandler { + - LOGGER : Logger {static} + - sessionCreationTimes : Map + - sessions : Map + + LoginHandler(sessions : Map, sessionCreationTimes : Map) + + handle(exchange : HttpExchange) + } + class LogoutHandler { + - LOGGER : Logger {static} + - sessionCreationTimes : Map + - sessions : Map + + LogoutHandler(sessions : Map, sessionCreationTimes : Map) + + handle(exchange : HttpExchange) + } +} +@enduml \ No newline at end of file diff --git a/server-session/pom.xml b/server-session/pom.xml index 160c4b6198a3..27dfe0be7bf4 100644 --- a/server-session/pom.xml +++ b/server-session/pom.xml @@ -9,19 +9,17 @@ java-design-patterns 1.26.0-SNAPSHOT - serversession + server-session org.junit.jupiter junit-jupiter-api - 5.10.2 test org.mockito mockito-core - 5.11.0 test 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 ce54c5f38008..4c693e51b820 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/App.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/App.java @@ -1,10 +1,12 @@ package com.iluwatar.sessionserver; +import com.sun.net.httpserver.HttpServer; import java.io.IOException; import java.net.InetSocketAddress; import java.time.Instant; -import java.util.*; -import com.sun.net.httpserver.HttpServer; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; import lombok.extern.slf4j.Slf4j; /** @@ -32,48 +34,54 @@ public class App { private static Map sessionCreationTimes = new HashMap<>(); private static final long SESSION_EXPIRATION_TIME = 10000; - public static void main(String[] args) throws IOException { - // Create HTTP server listening on port 8000 - HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0); + /** + * Main entry point. + * @param args arguments + * @throws IOException ex + */ + 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)); + // 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 the server + server.start(); - // Start background task to check for expired sessions - sessionExpirationTask(); + // Start background task to check for expired sessions + sessionExpirationTask(); - LOGGER.info("Server started. Listening on port 8080..."); - } + LOGGER.info("Server started. Listening on port 8080..."); + } - private static void sessionExpirationTask() { - new Thread(() -> { - while (true) { - try { - 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)) { - sessions.remove(entry.getKey()); - iterator.remove(); - } - } - } - } - LOGGER.info("Session expiration checker finished!"); - } catch (InterruptedException e) { - LOGGER.error("An error occurred: ", e); - Thread.currentThread().interrupt(); + private static void sessionExpirationTask() { + new Thread(() -> { + while (true) { + try { + 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)) { + sessions.remove(entry.getKey()); + iterator.remove(); } + } } - }).start(); - } + } + LOGGER.info("Session expiration checker finished!"); + } catch (InterruptedException e) { + LOGGER.error("An error occurred: ", e); + Thread.currentThread().interrupt(); + } + } + }).start(); + } } \ 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 b8f9b298b688..d327e74104a9 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java @@ -2,53 +2,52 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; -import lombok.extern.slf4j.Slf4j; -import org.slf4j.Logger; - import java.io.IOException; import java.io.OutputStream; import java.time.Instant; -import java.util.HashMap; import java.util.Map; import java.util.UUID; +import lombok.extern.slf4j.Slf4j; +/** + * LoginHandler. + */ @Slf4j public class LoginHandler implements HttpHandler { - private Map sessions; - private Map sessionCreationTimes; - - public LoginHandler(Map sessions, Map sessionCreationTimes) { - this.sessions = sessions; - this.sessionCreationTimes = sessionCreationTimes; + private Map sessions; + private Map sessionCreationTimes; + + public LoginHandler(Map sessions, Map sessionCreationTimes) { + this.sessions = sessions; + this.sessionCreationTimes = sessionCreationTimes; + } + + @Override + public void handle(HttpExchange exchange) { + // Generate session ID + String sessionId = UUID.randomUUID().toString(); + + // Store session data (simulated) + int newUser = sessions.size() + 1; + sessions.put(sessionId, newUser); + sessionCreationTimes.put(sessionId, Instant.now()); + LOGGER.info("User " + newUser + " created at time " + sessionCreationTimes.get(sessionId)); + + // Set session ID as cookie + exchange.getResponseHeaders().add("Set-Cookie", "sessionID=" + sessionId); + + // Send response + String response = "Login successful!\n" + "Session ID: " + sessionId; + try { + exchange.sendResponseHeaders(200, response.length()); + } catch (IOException e) { + LOGGER.error("An error occurred: ", e); } - - @Override - public void handle(HttpExchange exchange) { - // Generate session ID - String sessionID = UUID.randomUUID().toString(); - - // Store session data (simulated) - int newUser = sessions.size() + 1; - sessions.put(sessionID, newUser); - sessionCreationTimes.put(sessionID, Instant.now()); - LOGGER.info("User " + newUser + " created at time " + sessionCreationTimes.get(sessionID)); - - // Set session ID as cookie - exchange.getResponseHeaders().add("Set-Cookie", "sessionID=" + sessionID); - - // Send response - String response = "Login successful!\n" + - "Session ID: " + sessionID; - try { - exchange.sendResponseHeaders(200, response.length()); - } catch (IOException e) { - LOGGER.error("An error occurred: ", e); - } - try(OutputStream os = exchange.getResponseBody()) { - os.write(response.getBytes()); - } catch(IOException e) { - LOGGER.error("An error occurred: ", e); - } + try (OutputStream os = exchange.getResponseBody()) { + os.write(response.getBytes()); + } catch (IOException e) { + LOGGER.error("An error occurred: ", e); } + } } 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 b7a113b02b33..2dee93e27e74 100644 --- a/server-session/src/main/java/com/iluwatar/sessionserver/LogoutHandler.java +++ b/server-session/src/main/java/com/iluwatar/sessionserver/LogoutHandler.java @@ -2,58 +2,60 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; -import lombok.extern.slf4j.Slf4j; - import java.io.IOException; import java.io.OutputStream; import java.time.Instant; import java.util.Map; +import lombok.extern.slf4j.Slf4j; +/** + * LogoutHandler. + */ @Slf4j public class LogoutHandler implements HttpHandler { - private Map sessions; - private Map sessionCreationTimes; + private Map sessions; + private Map sessionCreationTimes; + + public LogoutHandler(Map sessions, Map sessionCreationTimes) { + this.sessions = sessions; + this.sessionCreationTimes = sessionCreationTimes; + } + + @Override + public void handle(HttpExchange exchange) { + // Get session ID from cookie + String sessionId = exchange.getRequestHeaders().getFirst("Cookie").replace("sessionID=", ""); + String currentSessionId = sessions.get(sessionId) == null ? null : sessionId; + + // Send response + + String response = ""; + if (currentSessionId == null) { + response += "Session has already expired!"; + } else { + response = "Logout successful!\n" + "Session ID: " + currentSessionId; + } + + //Remove session + if (currentSessionId != null) { + LOGGER.info("User " + sessions.get(currentSessionId) + " deleted!"); + } else { + LOGGER.info("User already deleted!"); + } + sessions.remove(sessionId); + sessionCreationTimes.remove(sessionId); - public LogoutHandler(Map sessions, Map sessionCreationTimes) { - this.sessions = sessions; - this.sessionCreationTimes = sessionCreationTimes; + try { + exchange.sendResponseHeaders(200, response.length()); + } catch (IOException e) { + LOGGER.error("An error has occurred: ", e); } - @Override - public void handle(HttpExchange exchange) { - // Get session ID from cookie - String sessionID = exchange.getRequestHeaders().getFirst("Cookie").replace("sessionID=", ""); - String currentSessionID = sessions.get(sessionID) == null ? null : sessionID; - - // Send response - - String response = ""; - if(currentSessionID == null) { - response += "Session has already expired!"; - } else { - response = "Logout successful!\n" + - "Session ID: " + currentSessionID; - } - - //Remove session - if(currentSessionID != null) - LOGGER.info("User " + sessions.get(currentSessionID) + " deleted!"); - else - LOGGER.info("User already deleted!"); - sessions.remove(sessionID); - sessionCreationTimes.remove(sessionID); - - try { - exchange.sendResponseHeaders(200, response.length()); - } catch(IOException e) { - LOGGER.error("An error has occurred: ", e); - } - - try(OutputStream os = exchange.getResponseBody()) { - os.write(response.getBytes()); - } catch(IOException e) { - LOGGER.error("An error has occurred: ", e); - } + try (OutputStream os = exchange.getResponseBody()) { + os.write(response.getBytes()); + } catch (IOException e) { + LOGGER.error("An error has occurred: ", e); } + } } diff --git a/server-session/src/test/java/com.iluwatar.sessionserver/LoginHandlerTest.java b/server-session/src/test/java/com.iluwatar.sessionserver/LoginHandlerTest.java index 8c976fa08ebf..4c07ef130b68 100644 --- a/server-session/src/test/java/com.iluwatar.sessionserver/LoginHandlerTest.java +++ b/server-session/src/test/java/com.iluwatar.sessionserver/LoginHandlerTest.java @@ -1,52 +1,59 @@ +package com.iluwatar.sessionserver; + +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 org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import com.iluwatar.sessionserver.LoginHandler; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.time.Instant; -import java.util.*; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +/** + * LoginHandlerTest. + */ public class LoginHandlerTest { - private LoginHandler loginHandler; - //private Headers headers; - private Map sessions; - private Map sessionCreationTimes; - - @Mock - private HttpExchange exchange; - - @BeforeEach - public void setUp() { - MockitoAnnotations.initMocks(this); - sessions = new HashMap<>(); - sessionCreationTimes = new HashMap<>(); - loginHandler = new LoginHandler(sessions, sessionCreationTimes); - } - - @Test - public void testHandle() throws IOException { - - //assemble - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); //Exchange object is mocked so OutputStream must be manually created - when(exchange.getResponseHeaders()).thenReturn(new Headers()); //Exchange object is mocked so Header object must be manually created - when(exchange.getResponseBody()).thenReturn(outputStream); - - //act - loginHandler.handle(exchange); - - //assert - String[] response = outputStream.toString().split("Session ID: "); - assertEquals(sessions.entrySet().toArray()[0].toString().split("=1")[0], response[1]); - } + private LoginHandler loginHandler; + //private Headers headers; + private Map sessions; + private Map sessionCreationTimes; + + @Mock + private HttpExchange exchange; + + /** + * Setup tests. + */ + @BeforeEach + public void setUp() { + MockitoAnnotations.initMocks(this); + sessions = new HashMap<>(); + sessionCreationTimes = new HashMap<>(); + loginHandler = new LoginHandler(sessions, sessionCreationTimes); + } + + @Test + public void testHandle() throws IOException { + + //assemble + ByteArrayOutputStream outputStream = + new ByteArrayOutputStream(); //Exchange object is mocked so OutputStream must be manually created + when(exchange.getResponseHeaders()).thenReturn( + new Headers()); //Exchange object is mocked so Header object must be manually created + when(exchange.getResponseBody()).thenReturn(outputStream); + + //act + loginHandler.handle(exchange); + + //assert + String[] response = outputStream.toString().split("Session ID: "); + assertEquals(sessions.entrySet().toArray()[0].toString().split("=1")[0], response[1]); + } } \ No newline at end of file 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 650cbc443629..60ab493b40c7 100644 --- a/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java +++ b/server-session/src/test/java/com.iluwatar.sessionserver/LogoutHandlerTest.java @@ -1,75 +1,83 @@ +package com.iluwatar.sessionserver; + +import static org.mockito.Mockito.when; + import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import com.iluwatar.sessionserver.LogoutHandler; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.time.Instant; import java.util.HashMap; import java.util.Map; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; -import static org.mockito.Mockito.when; - +/** + * LogoutHandlerTest. + */ public class LogoutHandlerTest { - private LogoutHandler logoutHandler; - private Headers headers; - private Map sessions; - private Map sessionCreationTimes; + private LogoutHandler logoutHandler; + private Headers headers; + private Map sessions; + private Map sessionCreationTimes; - @Mock - private HttpExchange exchange; + @Mock + private HttpExchange exchange; - @BeforeEach - public void setUp() { - MockitoAnnotations.initMocks(this); - sessions = new HashMap<>(); - sessionCreationTimes = new HashMap<>(); - logoutHandler = new LogoutHandler(sessions, sessionCreationTimes); - headers = new Headers(); - headers.add("Cookie", "sessionID=1234"); //Exchange object methods return Header Object but Exchange is mocked so Headers must be manually created - } + /** + * Setup tests. + */ + @BeforeEach + public void setUp() { + MockitoAnnotations.initMocks(this); + sessions = new HashMap<>(); + sessionCreationTimes = new HashMap<>(); + logoutHandler = new LogoutHandler(sessions, sessionCreationTimes); + headers = new Headers(); + headers.add("Cookie", + "sessionID=1234"); //Exchange object methods return Header Object but Exchange is mocked so Headers must be manually created + } - @Test - public void testHandler_SessionNotExpired() throws IOException { + @Test + public void testHandler_SessionNotExpired() throws IOException { - //assemble - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - 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); - when(exchange.getResponseBody()).thenReturn(outputStream); + //assemble + 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); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + when(exchange.getResponseBody()).thenReturn(outputStream); - //act - logoutHandler.handle(exchange); + //act + logoutHandler.handle(exchange); - //assert - String[] response = outputStream.toString().split("Session ID: "); - Assertions.assertEquals("1234", response[1]); - Assertions.assertFalse(sessions.containsKey(response)); - Assertions.assertFalse(sessionCreationTimes.containsKey(response)); - } + //assert + String[] response = outputStream.toString().split("Session ID: "); + Assertions.assertEquals("1234", response[1]); + Assertions.assertFalse(sessions.containsKey(response)); + Assertions.assertFalse(sessionCreationTimes.containsKey(response)); + } - @Test - public void testHandler_SessionExpired() throws IOException { + @Test + public void testHandler_SessionExpired() throws IOException { - //assemble - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - when(exchange.getRequestHeaders()).thenReturn(headers); - when(exchange.getResponseBody()).thenReturn(outputStream); + //assemble + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + when(exchange.getRequestHeaders()).thenReturn(headers); + when(exchange.getResponseBody()).thenReturn(outputStream); - //act - logoutHandler.handle(exchange); + //act + logoutHandler.handle(exchange); - //assert - String[] response = outputStream.toString().split("Session ID: "); - Assertions.assertEquals("Session has already expired!", response[0]); - Assertions.assertFalse(sessions.containsKey(response)); - Assertions.assertFalse(sessionCreationTimes.containsKey(response)); - } + //assert + String[] response = outputStream.toString().split("Session ID: "); + Assertions.assertEquals("Session has already expired!", response[0]); + Assertions.assertFalse(sessions.containsKey(response)); + Assertions.assertFalse(sessionCreationTimes.containsKey(response)); + } }