Skip to content

Commit 56dfd8c

Browse files
authored
fix: Fix server session (#2913)
* fix server session * fix version
1 parent 44071ab commit 56dfd8c

File tree

11 files changed

+463
-389
lines changed

11 files changed

+463
-389
lines changed

double-buffer/README.md

Lines changed: 189 additions & 165 deletions
Large diffs are not rendered by default.

double-buffer/src/main/java/com/iluwatar/doublebuffer/Pixel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@
3030
public enum Pixel {
3131

3232
WHITE,
33-
BLACK;
33+
BLACK
3434
}

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@
219219
<module>dynamic-proxy</module>
220220
<module>gateway</module>
221221
<module>slob</module>
222+
<module>server-session</module>
222223
</modules>
223224
<repositories>
224225
<repository>

server-session/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ title: Server Session
33
category: Behavioral
44
language: en
55
tag:
6-
- Session Management
7-
- Session Tracking
86
- Cookies
7+
- Session management
8+
- State tracking
99
---
1010

1111
## Also known as
@@ -20,7 +20,7 @@ Within the context of a client-server relationship, the server is responsible fo
2020

2121
Real-world example
2222

23-
> 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.
23+
> 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.
2424
2525
In plain words
2626

@@ -179,13 +179,13 @@ Sessions are often given a maximum time in which they will be maintained. The se
179179

180180
## Class diagram
181181

182-
![alt text](./etc/adapter.urm.png "Adapter class diagram")
182+
![Server Session class diagram](./etc/server-session.urm.puml "Server Session class diagram")
183183

184184
## Applicability
185185

186186
Use the Adapter pattern when
187187

188-
* When a user logs into a website or web application and you want to keep track of their authentication status.
188+
* When a user logs into a website or web application, and you want to keep track of their authentication status.
189189
* In e-commerce websites when you want to maintain the contents of a user's shopping cart across different pages and visits.
190190
* When you want to store user preferences and settings, such as language preferences, theme choices, or any other customizable options.
191191
* When you want to keep track of user activity and behavior on a website for the sake of analytics purposes.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
@startuml
2+
package com.iluwatar.sessionserver {
3+
class App {
4+
- LOGGER : Logger {static}
5+
- SESSION_EXPIRATION_TIME : long {static}
6+
- sessionCreationTimes : Map<String, Instant> {static}
7+
- sessions : Map<String, Integer> {static}
8+
+ App()
9+
+ main(args : String[]) {static}
10+
- sessionExpirationTask() {static}
11+
}
12+
class LoginHandler {
13+
- LOGGER : Logger {static}
14+
- sessionCreationTimes : Map<String, Instant>
15+
- sessions : Map<String, Integer>
16+
+ LoginHandler(sessions : Map<String, Integer>, sessionCreationTimes : Map<String, Instant>)
17+
+ handle(exchange : HttpExchange)
18+
}
19+
class LogoutHandler {
20+
- LOGGER : Logger {static}
21+
- sessionCreationTimes : Map<String, Instant>
22+
- sessions : Map<String, Integer>
23+
+ LogoutHandler(sessions : Map<String, Integer>, sessionCreationTimes : Map<String, Instant>)
24+
+ handle(exchange : HttpExchange)
25+
}
26+
}
27+
@enduml

server-session/pom.xml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,17 @@
99
<artifactId>java-design-patterns</artifactId>
1010
<version>1.26.0-SNAPSHOT</version>
1111
</parent>
12-
<artifactId>serversession</artifactId>
12+
<artifactId>server-session</artifactId>
1313

1414
<dependencies>
1515
<dependency>
1616
<groupId>org.junit.jupiter</groupId>
1717
<artifactId>junit-jupiter-api</artifactId>
18-
<version>5.10.2</version>
1918
<scope>test</scope>
2019
</dependency>
2120
<dependency>
2221
<groupId>org.mockito</groupId>
2322
<artifactId>mockito-core</artifactId>
24-
<version>5.11.0</version>
2523
<scope>test</scope>
2624
</dependency>
2725
</dependencies>
Lines changed: 47 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package com.iluwatar.sessionserver;
22

3+
import com.sun.net.httpserver.HttpServer;
34
import java.io.IOException;
45
import java.net.InetSocketAddress;
56
import java.time.Instant;
6-
import java.util.*;
7-
import com.sun.net.httpserver.HttpServer;
7+
import java.util.HashMap;
8+
import java.util.Iterator;
9+
import java.util.Map;
810
import lombok.extern.slf4j.Slf4j;
911

1012
/**
@@ -32,48 +34,54 @@ public class App {
3234
private static Map<String, Instant> sessionCreationTimes = new HashMap<>();
3335
private static final long SESSION_EXPIRATION_TIME = 10000;
3436

35-
public static void main(String[] args) throws IOException {
36-
// Create HTTP server listening on port 8000
37-
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
37+
/**
38+
* Main entry point.
39+
* @param args arguments
40+
* @throws IOException ex
41+
*/
42+
public static void main(String[] args) throws IOException {
43+
// Create HTTP server listening on port 8000
44+
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
3845

39-
// Set up session management endpoints
40-
server.createContext("/login", new LoginHandler(sessions, sessionCreationTimes));
41-
server.createContext("/logout", new LogoutHandler(sessions, sessionCreationTimes));
46+
// Set up session management endpoints
47+
server.createContext("/login", new LoginHandler(sessions, sessionCreationTimes));
48+
server.createContext("/logout", new LogoutHandler(sessions, sessionCreationTimes));
4249

43-
// Start the server
44-
server.start();
50+
// Start the server
51+
server.start();
4552

46-
// Start background task to check for expired sessions
47-
sessionExpirationTask();
53+
// Start background task to check for expired sessions
54+
sessionExpirationTask();
4855

49-
LOGGER.info("Server started. Listening on port 8080...");
50-
}
56+
LOGGER.info("Server started. Listening on port 8080...");
57+
}
5158

52-
private static void sessionExpirationTask() {
53-
new Thread(() -> {
54-
while (true) {
55-
try {
56-
LOGGER.info("Session expiration checker started...");
57-
Thread.sleep(SESSION_EXPIRATION_TIME); // Sleep for expiration time
58-
Instant currentTime = Instant.now();
59-
synchronized (sessions) {
60-
synchronized (sessionCreationTimes) {
61-
Iterator<Map.Entry<String, Instant>> iterator = sessionCreationTimes.entrySet().iterator();
62-
while (iterator.hasNext()) {
63-
Map.Entry<String, Instant> entry = iterator.next();
64-
if (entry.getValue().plusMillis(SESSION_EXPIRATION_TIME).isBefore(currentTime)) {
65-
sessions.remove(entry.getKey());
66-
iterator.remove();
67-
}
68-
}
69-
}
70-
}
71-
LOGGER.info("Session expiration checker finished!");
72-
} catch (InterruptedException e) {
73-
LOGGER.error("An error occurred: ", e);
74-
Thread.currentThread().interrupt();
59+
private static void sessionExpirationTask() {
60+
new Thread(() -> {
61+
while (true) {
62+
try {
63+
LOGGER.info("Session expiration checker started...");
64+
Thread.sleep(SESSION_EXPIRATION_TIME); // Sleep for expiration time
65+
Instant currentTime = Instant.now();
66+
synchronized (sessions) {
67+
synchronized (sessionCreationTimes) {
68+
Iterator<Map.Entry<String, Instant>> iterator =
69+
sessionCreationTimes.entrySet().iterator();
70+
while (iterator.hasNext()) {
71+
Map.Entry<String, Instant> entry = iterator.next();
72+
if (entry.getValue().plusMillis(SESSION_EXPIRATION_TIME).isBefore(currentTime)) {
73+
sessions.remove(entry.getKey());
74+
iterator.remove();
7575
}
76+
}
7677
}
77-
}).start();
78-
}
78+
}
79+
LOGGER.info("Session expiration checker finished!");
80+
} catch (InterruptedException e) {
81+
LOGGER.error("An error occurred: ", e);
82+
Thread.currentThread().interrupt();
83+
}
84+
}
85+
}).start();
86+
}
7987
}

server-session/src/main/java/com/iluwatar/sessionserver/LoginHandler.java

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,53 +2,52 @@
22

33
import com.sun.net.httpserver.HttpExchange;
44
import com.sun.net.httpserver.HttpHandler;
5-
import lombok.extern.slf4j.Slf4j;
6-
import org.slf4j.Logger;
7-
85
import java.io.IOException;
96
import java.io.OutputStream;
107
import java.time.Instant;
11-
import java.util.HashMap;
128
import java.util.Map;
139
import java.util.UUID;
10+
import lombok.extern.slf4j.Slf4j;
1411

12+
/**
13+
* LoginHandler.
14+
*/
1515
@Slf4j
1616
public class LoginHandler implements HttpHandler {
1717

18-
private Map<String, Integer> sessions;
19-
private Map<String, Instant> sessionCreationTimes;
20-
21-
public LoginHandler(Map<String, Integer> sessions, Map<String, Instant> sessionCreationTimes) {
22-
this.sessions = sessions;
23-
this.sessionCreationTimes = sessionCreationTimes;
18+
private Map<String, Integer> sessions;
19+
private Map<String, Instant> sessionCreationTimes;
20+
21+
public LoginHandler(Map<String, Integer> sessions, Map<String, Instant> sessionCreationTimes) {
22+
this.sessions = sessions;
23+
this.sessionCreationTimes = sessionCreationTimes;
24+
}
25+
26+
@Override
27+
public void handle(HttpExchange exchange) {
28+
// Generate session ID
29+
String sessionId = UUID.randomUUID().toString();
30+
31+
// Store session data (simulated)
32+
int newUser = sessions.size() + 1;
33+
sessions.put(sessionId, newUser);
34+
sessionCreationTimes.put(sessionId, Instant.now());
35+
LOGGER.info("User " + newUser + " created at time " + sessionCreationTimes.get(sessionId));
36+
37+
// Set session ID as cookie
38+
exchange.getResponseHeaders().add("Set-Cookie", "sessionID=" + sessionId);
39+
40+
// Send response
41+
String response = "Login successful!\n" + "Session ID: " + sessionId;
42+
try {
43+
exchange.sendResponseHeaders(200, response.length());
44+
} catch (IOException e) {
45+
LOGGER.error("An error occurred: ", e);
2446
}
25-
26-
@Override
27-
public void handle(HttpExchange exchange) {
28-
// Generate session ID
29-
String sessionID = UUID.randomUUID().toString();
30-
31-
// Store session data (simulated)
32-
int newUser = sessions.size() + 1;
33-
sessions.put(sessionID, newUser);
34-
sessionCreationTimes.put(sessionID, Instant.now());
35-
LOGGER.info("User " + newUser + " created at time " + sessionCreationTimes.get(sessionID));
36-
37-
// Set session ID as cookie
38-
exchange.getResponseHeaders().add("Set-Cookie", "sessionID=" + sessionID);
39-
40-
// Send response
41-
String response = "Login successful!\n" +
42-
"Session ID: " + sessionID;
43-
try {
44-
exchange.sendResponseHeaders(200, response.length());
45-
} catch (IOException e) {
46-
LOGGER.error("An error occurred: ", e);
47-
}
48-
try(OutputStream os = exchange.getResponseBody()) {
49-
os.write(response.getBytes());
50-
} catch(IOException e) {
51-
LOGGER.error("An error occurred: ", e);
52-
}
47+
try (OutputStream os = exchange.getResponseBody()) {
48+
os.write(response.getBytes());
49+
} catch (IOException e) {
50+
LOGGER.error("An error occurred: ", e);
5351
}
52+
}
5453
}

0 commit comments

Comments
 (0)