mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-19 19:26:36 +00:00
* #2848 Moved project from private project directory to forked repo * #2848 Requested changes implemented
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
package com.iluwatar.sessionserver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
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
|
||||
* requests are isolated events independent of previous requests. In order to create sessions during
|
||||
* user-access for a particular web application various methods can be used, such as cookies. Cookies
|
||||
* are a small piece of data that can be sent between client and server on every request and response
|
||||
* so that the server can "remember" the previous requests. In general cookies can either store the session
|
||||
* data or the cookie can store a session identifier and be used to access appropriate data from a persistent
|
||||
* storage. In the latter case the session data is stored on the server-side and appropriate data is
|
||||
* identified by the cookie sent from a client's request.
|
||||
* This project demonstrates the latter case.
|
||||
* In the following example the ({@link App}) class starts a server and assigns ({@link LoginHandler})
|
||||
* class to handle login request. When a user logs in a session identifier is created and stored for future
|
||||
* requests in a list. When a user logs out the session identifier is deleted from the list along with
|
||||
* the appropriate user session data, which is handle by the ({@link LogoutHandler}) class.
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
public class App {
|
||||
|
||||
// Map to store session data (simulated using a HashMap)
|
||||
private static Map<String, Integer> sessions = new HashMap<>();
|
||||
private static Map<String, Instant> 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);
|
||||
|
||||
// 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(() -> {
|
||||
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<Map.Entry<String, Instant>> iterator = sessionCreationTimes.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, Instant> 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();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.iluwatar.sessionserver;
|
||||
|
||||
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;
|
||||
|
||||
@Slf4j
|
||||
public class LoginHandler implements HttpHandler {
|
||||
|
||||
private Map<String, Integer> sessions;
|
||||
private Map<String, Instant> sessionCreationTimes;
|
||||
|
||||
public LoginHandler(Map<String, Integer> sessions, Map<String, Instant> 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);
|
||||
}
|
||||
try(OutputStream os = exchange.getResponseBody()) {
|
||||
os.write(response.getBytes());
|
||||
} catch(IOException e) {
|
||||
LOGGER.error("An error occurred: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.iluwatar.sessionserver;
|
||||
|
||||
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;
|
||||
|
||||
@Slf4j
|
||||
public class LogoutHandler implements HttpHandler {
|
||||
|
||||
private Map<String, Integer> sessions;
|
||||
private Map<String, Instant> sessionCreationTimes;
|
||||
|
||||
public LogoutHandler(Map<String, Integer> sessions, Map<String, Instant> 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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user