Core server component of Eclipse Jetty web server providing HTTP server functionality, request handling, and connection management
—
Session management provides HTTP session support with creation, invalidation, attribute storage, and timeout management for maintaining user state across requests.
The core session interface for managing HTTP session state.
public interface Session extends Attributes {
// Session identification
String getId();
Context getContext();
// Timing information
long getCreationTime();
long getLastAccessedTime();
// Session lifecycle
void setMaxInactiveInterval(int interval);
int getMaxInactiveInterval();
void invalidate();
boolean isNew();
boolean isValid();
// Attribute management (from Attributes interface)
Object getAttribute(String name);
void setAttribute(String name, Object value);
void removeAttribute(String name);
Set<String> getAttributeNameSet();
void clearAttributes();
}public class SessionDemoHandler extends Handler.Abstract {
@Override
public boolean handle(Request request, Response response, Callback callback)
throws Exception {
// Get existing session or create new one
Session session = request.getSession(true);
// Check if this is a new session
if (session.isNew()) {
System.out.println("New session created: " + session.getId());
session.setAttribute("createdAt", Instant.now());
}
// Update visit count
Integer visitCount = (Integer) session.getAttribute("visitCount");
if (visitCount == null) {
visitCount = 0;
}
visitCount++;
session.setAttribute("visitCount", visitCount);
// Update last visit time
session.setAttribute("lastVisit", Instant.now());
// Set session timeout (30 minutes)
session.setMaxInactiveInterval(30 * 60);
// Generate response with session info
response.setStatus(200);
response.getHeaders().put("Content-Type", "application/json");
String json = createSessionInfoJson(session);
response.write(true, ByteBuffer.wrap(json.getBytes()), callback);
return true;
}
private String createSessionInfoJson(Session session) {
Map<String, Object> sessionInfo = new HashMap<>();
sessionInfo.put("sessionId", session.getId());
sessionInfo.put("isNew", session.isNew());
sessionInfo.put("creationTime", session.getCreationTime());
sessionInfo.put("lastAccessedTime", session.getLastAccessedTime());
sessionInfo.put("maxInactiveInterval", session.getMaxInactiveInterval());
// Add session attributes
Map<String, Object> attributes = new HashMap<>();
for (String name : session.getAttributeNameSet()) {
Object value = session.getAttribute(name);
if (value instanceof Serializable) {
attributes.put(name, value.toString());
}
}
sessionInfo.put("attributes", attributes);
// Convert to JSON (simplified)
return toJson(sessionInfo);
}
private String toJson(Map<String, Object> map) {
// Simplified JSON conversion
StringBuilder json = new StringBuilder("{");
boolean first = true;
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (!first) json.append(",");
json.append("\"").append(entry.getKey()).append("\":");
json.append("\"").append(entry.getValue()).append("\"");
first = false;
}
json.append("}");
return json.toString();
}
}Handler that provides session support to child handlers.
public class SessionHandler extends Handler.Wrapper {
// Session configuration
public SessionCache getSessionCache();
public void setSessionCache(SessionCache sessionCache);
public SessionDataStore getSessionDataStore();
public void setSessionDataStore(SessionDataStore sessionDataStore);
// Session ID configuration
public SessionIdManager getSessionIdManager();
public void setSessionIdManager(SessionIdManager sessionIdManager);
// Cookie configuration
public SessionCookieConfig getSessionCookieConfig();
public void setSessionCookieConfig(SessionCookieConfig config);
// Timeout configuration
public int getMaxInactiveInterval();
public void setMaxInactiveInterval(int maxInactiveInterval);
// Session tracking modes
public Set<SessionTrackingMode> getEffectiveSessionTrackingModes();
public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes);
// Session lifecycle
public Session newSession(Request request);
public void complete(Session session);
}public class SessionConfiguration {
public void configureSession(Server server) {
// Create session handler
SessionHandler sessionHandler = new SessionHandler();
// Configure session timeout (30 minutes)
sessionHandler.setMaxInactiveInterval(30 * 60);
// Configure session cookie
SessionCookieConfig cookieConfig = sessionHandler.getSessionCookieConfig();
cookieConfig.setName("JSESSIONID");
cookieConfig.setPath("/");
cookieConfig.setHttpOnly(true);
cookieConfig.setSecure(true); // HTTPS only
cookieConfig.setMaxAge(24 * 60 * 60); // 24 hours
// Set session tracking mode
Set<SessionTrackingMode> trackingModes = EnumSet.of(SessionTrackingMode.COOKIE);
sessionHandler.setSessionTrackingModes(trackingModes);
// Set application handler as child
sessionHandler.setHandler(new ApplicationHandler());
server.setHandler(sessionHandler);
}
}public class DatabaseSessionDataStore extends AbstractSessionDataStore {
private final DataSource dataSource;
public DatabaseSessionDataStore(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public SessionData doLoad(String id) throws Exception {
try (Connection conn = dataSource.getConnection()) {
String sql = "SELECT * FROM sessions WHERE session_id = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, id);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return deserializeSessionData(rs);
}
}
}
}
return null;
}
@Override
public boolean doStore(String id, SessionData data, long lastSaveTime) throws Exception {
try (Connection conn = dataSource.getConnection()) {
String sql = "INSERT INTO sessions (session_id, creation_time, last_access_time, " +
"max_inactive_interval, attributes) VALUES (?, ?, ?, ?, ?) " +
"ON DUPLICATE KEY UPDATE last_access_time = ?, attributes = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, id);
stmt.setLong(2, data.getCreated());
stmt.setLong(3, data.getLastAccessed());
stmt.setInt(4, data.getMaxInactiveInterval());
stmt.setBytes(5, serializeAttributes(data.getAttributes()));
stmt.setLong(6, data.getLastAccessed());
stmt.setBytes(7, serializeAttributes(data.getAttributes()));
return stmt.executeUpdate() > 0;
}
}
}
@Override
public boolean doDelete(String id) throws Exception {
try (Connection conn = dataSource.getConnection()) {
String sql = "DELETE FROM sessions WHERE session_id = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, id);
return stmt.executeUpdate() > 0;
}
}
}
@Override
public Set<String> doCheckExpired(Set<String> candidates, long time) throws Exception {
Set<String> expired = new HashSet<>();
try (Connection conn = dataSource.getConnection()) {
String sql = "SELECT session_id FROM sessions WHERE " +
"last_access_time + (max_inactive_interval * 1000) < ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setLong(1, time);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
expired.add(rs.getString("session_id"));
}
}
}
}
return expired;
}
@Override
public Set<String> doGetExpired(long time) throws Exception {
return doCheckExpired(Collections.emptySet(), time);
}
@Override
public void doCleanOrphans(long time) throws Exception {
try (Connection conn = dataSource.getConnection()) {
String sql = "DELETE FROM sessions WHERE " +
"last_access_time + (max_inactive_interval * 1000) < ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setLong(1, time);
int deleted = stmt.executeUpdate();
System.out.println("Cleaned " + deleted + " orphaned sessions");
}
}
}
private SessionData deserializeSessionData(ResultSet rs) throws SQLException {
SessionData data = new SessionData(
rs.getString("session_id"),
"/", // context path
"localhost", // virtual host
rs.getLong("creation_time"),
rs.getLong("last_access_time"),
rs.getLong("last_access_time"),
rs.getInt("max_inactive_interval")
);
// Deserialize attributes
byte[] attributeBytes = rs.getBytes("attributes");
if (attributeBytes != null) {
Map<String, Object> attributes = deserializeAttributes(attributeBytes);
data.putAllAttributes(attributes);
}
return data;
}
private byte[] serializeAttributes(Map<String, Object> attributes) {
// Implement serialization (e.g., using Java serialization or JSON)
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(attributes);
return baos.toByteArray();
} catch (IOException e) {
throw new RuntimeException("Failed to serialize session attributes", e);
}
}
private Map<String, Object> deserializeAttributes(byte[] data) {
// Implement deserialization
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bais)) {
return (Map<String, Object>) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("Failed to deserialize session attributes", e);
}
}
}public class SessionCacheConfiguration {
public SessionHandler createSessionHandler(DataSource dataSource) {
SessionHandler sessionHandler = new SessionHandler();
// Create session ID manager
DefaultSessionIdManager sessionIdManager = new DefaultSessionIdManager(server);
sessionIdManager.setWorkerName("node1");
sessionHandler.setSessionIdManager(sessionIdManager);
// Create session data store
DatabaseSessionDataStore sessionDataStore = new DatabaseSessionDataStore(dataSource);
sessionDataStore.setGracePeriodSec(3600); // 1 hour grace period
sessionDataStore.setSavePeriodSec(0); // Save on every change
// Create session cache
DefaultSessionCache sessionCache = new DefaultSessionCache(sessionHandler);
sessionCache.setSessionDataStore(sessionDataStore);
sessionCache.setEvictionPolicy(SessionCache.NEVER_EVICT);
sessionCache.setSaveOnCreate(true);
sessionCache.setSaveOnInactiveEviction(true);
sessionCache.setRemoveUnloadableSessions(true);
sessionHandler.setSessionCache(sessionCache);
return sessionHandler;
}
}public class SecureSessionHandler extends SessionHandler {
@Override
protected void doStart() throws Exception {
super.doStart();
// Configure secure session cookie
SessionCookieConfig cookieConfig = getSessionCookieConfig();
cookieConfig.setSecure(true); // HTTPS only
cookieConfig.setHttpOnly(true); // No JavaScript access
cookieConfig.setSameSite(SameSite.STRICT); // CSRF protection
// Use random session ID generation
SessionIdManager idManager = getSessionIdManager();
if (idManager instanceof DefaultSessionIdManager) {
DefaultSessionIdManager defaultIdManager = (DefaultSessionIdManager) idManager;
defaultIdManager.setWorkerName(null); // Use random worker name
}
}
@Override
public Session newSession(Request request) {
Session session = super.newSession(request);
// Add security attributes
session.setAttribute("remoteAddr",
request.getConnectionMetaData().getRemoteSocketAddress());
session.setAttribute("userAgent", request.getHeaders().get("User-Agent"));
return session;
}
@Override
protected void sessionInactivityTimer(Session session) {
// Log session timeout for security monitoring
System.out.println("Session timeout: " + session.getId() +
" from " + session.getAttribute("remoteAddr"));
super.sessionInactivityTimer(session);
}
}public class SessionValidationHandler extends Handler.Wrapper {
@Override
public boolean handle(Request request, Response response, Callback callback)
throws Exception {
Session session = request.getSession(false);
if (session != null && !isValidSession(session, request)) {
// Invalid session, invalidate and create new
session.invalidate();
session = request.getSession(true);
}
return super.handle(request, response, callback);
}
private boolean isValidSession(Session session, Request request) {
// Check if session is from same IP address
String sessionAddr = (String) session.getAttribute("remoteAddr");
String currentAddr = request.getConnectionMetaData().getRemoteSocketAddress().toString();
if (sessionAddr != null && !sessionAddr.equals(currentAddr)) {
System.err.println("Session hijack attempt detected: " + session.getId());
return false;
}
// Check user agent
String sessionUserAgent = (String) session.getAttribute("userAgent");
String currentUserAgent = request.getHeaders().get("User-Agent");
if (sessionUserAgent != null && !sessionUserAgent.equals(currentUserAgent)) {
System.err.println("User agent mismatch for session: " + session.getId());
return false;
}
return true;
}
}public class ClusteredSessionConfiguration {
public void configureClusteredSessions(Server server, String[] nodes) {
// Create shared session ID manager
DefaultSessionIdManager sessionIdManager = new DefaultSessionIdManager(server);
sessionIdManager.setWorkerName(getNodeName());
server.addBean(sessionIdManager);
// Create clustered session data store
ClusteredSessionDataStore dataStore = new ClusteredSessionDataStore(nodes);
// Configure session handler
SessionHandler sessionHandler = new SessionHandler();
sessionHandler.setSessionIdManager(sessionIdManager);
// Create session cache with clustering support
DefaultSessionCache sessionCache = new DefaultSessionCache(sessionHandler);
sessionCache.setSessionDataStore(dataStore);
sessionCache.setEvictionPolicy(SessionCache.EVICT_ON_SESSION_EXIT);
sessionHandler.setSessionCache(sessionCache);
// Set application handler
sessionHandler.setHandler(new ApplicationHandler());
server.setHandler(sessionHandler);
}
private String getNodeName() {
// Generate unique node identifier
return "node-" + System.currentTimeMillis();
}
}public class SessionStatisticsHandler extends Handler.Wrapper {
private final AtomicLong sessionsCreated = new AtomicLong();
private final AtomicLong sessionsDestroyed = new AtomicLong();
private final AtomicLong sessionsTimeout = new AtomicLong();
private final Map<String, Long> sessionStartTimes = new ConcurrentHashMap<>();
@Override
public boolean handle(Request request, Response response, Callback callback)
throws Exception {
Session session = request.getSession(false);
if (session != null) {
if (session.isNew()) {
sessionsCreated.incrementAndGet();
sessionStartTimes.put(session.getId(), System.currentTimeMillis());
// Add session listener
session.setAttribute("statisticsHandler", this);
}
}
return super.handle(request, response, callback);
}
public void onSessionDestroyed(String sessionId) {
sessionsDestroyed.incrementAndGet();
sessionStartTimes.remove(sessionId);
}
public void onSessionTimeout(String sessionId) {
sessionsTimeout.incrementAndGet();
sessionStartTimes.remove(sessionId);
}
public long getSessionsCreated() {
return sessionsCreated.get();
}
public long getSessionsDestroyed() {
return sessionsDestroyed.get();
}
public long getSessionsTimeout() {
return sessionsTimeout.get();
}
public long getActiveSessions() {
return sessionsCreated.get() - sessionsDestroyed.get();
}
public double getAverageSessionDuration() {
long totalDuration = 0;
long count = 0;
for (Long startTime : sessionStartTimes.values()) {
totalDuration += System.currentTimeMillis() - startTime;
count++;
}
return count > 0 ? (double) totalDuration / count : 0.0;
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-eclipse-jetty--jetty-server