Java Servlet API specification defining core interfaces and classes for web application development
—
The Java Servlet API provides a comprehensive event-driven architecture through various listener interfaces. This enables applications to respond to lifecycle events, attribute changes, and other significant events in the servlet container.
/**
* Listener interface for receiving notifications about ServletContext
* lifecycle changes (application startup and shutdown).
*/
public interface ServletContextListener extends EventListener {
/**
* Called when the ServletContext is initialized (application startup).
* This method is invoked before any servlets, filters, or listeners
* are initialized.
*/
void contextInitialized(ServletContextEvent sce);
/**
* Called when the ServletContext is about to be destroyed (application shutdown).
* This method is invoked after all servlets and filters have been destroyed.
*/
void contextDestroyed(ServletContextEvent sce);
}/**
* Listener interface for receiving notifications about ServletContext
* attribute changes.
*/
public interface ServletContextAttributeListener extends EventListener {
/**
* Called when an attribute is added to the ServletContext.
*/
void attributeAdded(ServletContextAttributeEvent event);
/**
* Called when an attribute is removed from the ServletContext.
*/
void attributeRemoved(ServletContextAttributeEvent event);
/**
* Called when an attribute in the ServletContext is replaced.
*/
void attributeReplaced(ServletContextAttributeEvent event);
}/**
* Event class for ServletContext lifecycle events.
*/
public class ServletContextEvent extends java.util.EventObject {
/**
* Create a ServletContextEvent from the given ServletContext.
*/
public ServletContextEvent(ServletContext source) {
super(source);
}
/**
* Get the ServletContext that changed.
*/
public ServletContext getServletContext() {
return (ServletContext) super.getSource();
}
}
/**
* Event class for ServletContext attribute changes.
*/
public class ServletContextAttributeEvent extends ServletContextEvent {
private String name;
private Object value;
/**
* Create a ServletContextAttributeEvent.
*/
public ServletContextAttributeEvent(ServletContext source, String name, Object value) {
super(source);
this.name = name;
this.value = value;
}
/**
* Get the name of the attribute that changed.
*/
public String getName() {
return name;
}
/**
* Get the value of the attribute.
* For attributeAdded: the value that was added.
* For attributeRemoved: the value that was removed.
* For attributeReplaced: the old value that was replaced.
*/
public Object getValue() {
return value;
}
}/**
* Listener interface for receiving notifications about ServletRequest
* lifecycle events (request initialization and destruction).
*/
public interface ServletRequestListener extends EventListener {
/**
* Called when a ServletRequest is about to go out of scope.
*/
void requestDestroyed(ServletRequestEvent sre);
/**
* Called when a ServletRequest is about to come into scope.
*/
void requestInitialized(ServletRequestEvent sre);
}/**
* Listener interface for receiving notifications about ServletRequest
* attribute changes.
*/
public interface ServletRequestAttributeListener extends EventListener {
/**
* Called when an attribute is added to the ServletRequest.
*/
void attributeAdded(ServletRequestAttributeEvent srae);
/**
* Called when an attribute is removed from the ServletRequest.
*/
void attributeRemoved(ServletRequestAttributeEvent srae);
/**
* Called when an attribute in the ServletRequest is replaced.
*/
void attributeReplaced(ServletRequestAttributeEvent srae);
}/**
* Event class for ServletRequest lifecycle events.
*/
public class ServletRequestEvent extends java.util.EventObject {
private ServletContext servletContext;
/**
* Create a ServletRequestEvent.
*/
public ServletRequestEvent(ServletContext sc, ServletRequest request) {
super(request);
this.servletContext = sc;
}
/**
* Get the ServletRequest that is changing.
*/
public ServletRequest getServletRequest() {
return (ServletRequest) super.getSource();
}
/**
* Get the ServletContext of the request.
*/
public ServletContext getServletContext() {
return servletContext;
}
}
/**
* Event class for ServletRequest attribute changes.
*/
public class ServletRequestAttributeEvent extends ServletRequestEvent {
private String name;
private Object value;
/**
* Create a ServletRequestAttributeEvent.
*/
public ServletRequestAttributeEvent(ServletContext sc, ServletRequest request,
String name, Object value) {
super(sc, request);
this.name = name;
this.value = value;
}
/**
* Get the name of the attribute that changed.
*/
public String getName() {
return name;
}
/**
* Get the value of the attribute.
*/
public Object getValue() {
return value;
}
}/**
* Listener interface for receiving notifications about HttpSession
* lifecycle changes (session creation and destruction).
*/
public interface HttpSessionListener extends EventListener {
/**
* Called when an HttpSession is created.
*/
void sessionCreated(HttpSessionEvent se);
/**
* Called when an HttpSession is destroyed (invalidated or timed out).
*/
void sessionDestroyed(HttpSessionEvent se);
}/**
* Listener interface for receiving notifications about HttpSession
* attribute changes.
*/
public interface HttpSessionAttributeListener extends EventListener {
/**
* Called when an attribute is added to an HttpSession.
*/
void attributeAdded(HttpSessionBindingEvent event);
/**
* Called when an attribute is removed from an HttpSession.
*/
void attributeRemoved(HttpSessionBindingEvent event);
/**
* Called when an attribute in an HttpSession is replaced.
*/
void attributeReplaced(HttpSessionBindingEvent event);
}/**
* Listener interface for objects that need to be notified when
* they are bound/unbound from a session that is being passivated/activated.
*/
public interface HttpSessionActivationListener extends EventListener {
/**
* Called when the session containing this object is about to be passivated.
* Objects can use this notification to release resources or prepare for serialization.
*/
void sessionWillPassivate(HttpSessionEvent se);
/**
* Called when the session containing this object has been activated.
* Objects can use this notification to reacquire resources.
*/
void sessionDidActivate(HttpSessionEvent se);
}/**
* Listener interface for objects that want to be notified when
* they are bound to or unbound from an HttpSession.
*/
public interface HttpSessionBindingListener extends EventListener {
/**
* Called when the object is bound to a session.
*/
void valueBound(HttpSessionBindingEvent event);
/**
* Called when the object is unbound from a session.
*/
void valueUnbound(HttpSessionBindingEvent event);
}/**
* Listener interface for receiving notifications about HttpSession ID changes.
*/
public interface HttpSessionIdListener extends EventListener {
/**
* Called when an HttpSession ID is changed.
*/
void sessionIdChanged(HttpSessionEvent event, String oldSessionId);
}/**
* Event class for HttpSession lifecycle and ID change events.
*/
public class HttpSessionEvent extends java.util.EventObject {
/**
* Create an HttpSessionEvent from the given HttpSession.
*/
public HttpSessionEvent(HttpSession source) {
super(source);
}
/**
* Get the HttpSession that changed.
*/
public HttpSession getSession() {
return (HttpSession) super.getSource();
}
}
/**
* Event class for HttpSession attribute binding operations.
*/
public class HttpSessionBindingEvent extends HttpSessionEvent {
private String name;
private Object value;
/**
* Create an HttpSessionBindingEvent.
*/
public HttpSessionBindingEvent(HttpSession session, String name) {
super(session);
this.name = name;
}
/**
* Create an HttpSessionBindingEvent with a value.
*/
public HttpSessionBindingEvent(HttpSession session, String name, Object value) {
super(session);
this.name = name;
this.value = value;
}
/**
* Get the name of the attribute that was bound/unbound.
*/
public String getName() {
return name;
}
/**
* Get the value of the attribute.
*/
public Object getValue() {
return value;
}
}/**
* Comprehensive application lifecycle listener that manages startup and shutdown
*/
@WebListener
public class ApplicationLifecycleManager implements ServletContextListener,
ServletContextAttributeListener {
private static final Logger logger = LoggerFactory.getLogger(ApplicationLifecycleManager.class);
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
logger.info("Application startup initiated");
try {
// Initialize application components
initializeDatabase(context);
initializeScheduler(context);
initializeCaching(context);
initializeExternalServices(context);
// Set application metadata
context.setAttribute("app.startTime", System.currentTimeMillis());
context.setAttribute("app.version", getClass().getPackage().getImplementationVersion());
context.setAttribute("app.status", "RUNNING");
// Initialize application configuration
Properties config = loadApplicationConfig();
context.setAttribute("app.config", config);
// Start background tasks
startBackgroundTasks(context);
logger.info("Application startup completed successfully");
} catch (Exception e) {
logger.error("Application startup failed", e);
context.setAttribute("app.status", "FAILED");
throw new RuntimeException("Application initialization failed", e);
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
logger.info("Application shutdown initiated");
try {
// Set shutdown status
context.setAttribute("app.status", "SHUTTING_DOWN");
// Stop background tasks
stopBackgroundTasks(context);
// Shutdown application components in reverse order
shutdownExternalServices(context);
shutdownCaching(context);
shutdownScheduler(context);
shutdownDatabase(context);
// Clear application attributes
clearApplicationAttributes(context);
logger.info("Application shutdown completed successfully");
} catch (Exception e) {
logger.error("Error during application shutdown", e);
}
}
@Override
public void attributeAdded(ServletContextAttributeEvent event) {
String name = event.getName();
Object value = event.getValue();
logger.debug("ServletContext attribute added: {} = {}", name, value);
// Monitor critical attributes
if ("app.status".equals(name)) {
logger.info("Application status changed to: {}", value);
}
}
@Override
public void attributeRemoved(ServletContextAttributeEvent event) {
String name = event.getName();
Object value = event.getValue();
logger.debug("ServletContext attribute removed: {} (was: {})", name, value);
}
@Override
public void attributeReplaced(ServletContextAttributeEvent event) {
String name = event.getName();
Object newValue = event.getServletContext().getAttribute(name);
Object oldValue = event.getValue();
logger.debug("ServletContext attribute replaced: {} = {} (was: {})",
name, newValue, oldValue);
}
private void initializeDatabase(ServletContext context) {
// Initialize database connections and connection pool
logger.info("Initializing database connections");
DataSource dataSource = createDataSource();
context.setAttribute("app.dataSource", dataSource);
// Test database connection
try (Connection conn = dataSource.getConnection()) {
logger.info("Database connection established successfully");
} catch (SQLException e) {
throw new RuntimeException("Failed to establish database connection", e);
}
}
private void initializeScheduler(ServletContext context) {
logger.info("Initializing task scheduler");
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);
context.setAttribute("app.scheduler", scheduler);
// Schedule periodic maintenance tasks
scheduler.scheduleAtFixedRate(() -> {
logger.debug("Running periodic maintenance");
performMaintenance();
}, 1, 1, TimeUnit.HOURS);
}
private void initializeCaching(ServletContext context) {
logger.info("Initializing cache system");
// Initialize cache manager (e.g., EhCache, Redis, etc.)
CacheManager cacheManager = createCacheManager();
context.setAttribute("app.cacheManager", cacheManager);
}
private void initializeExternalServices(ServletContext context) {
logger.info("Initializing external service connections");
// Initialize HTTP clients, web service clients, etc.
HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(30))
.build();
context.setAttribute("app.httpClient", httpClient);
}
private void startBackgroundTasks(ServletContext context) {
logger.info("Starting background tasks");
ScheduledExecutorService scheduler =
(ScheduledExecutorService) context.getAttribute("app.scheduler");
// Start application-specific background tasks
scheduler.scheduleAtFixedRate(new SessionCleanupTask(), 0, 30, TimeUnit.MINUTES);
scheduler.scheduleAtFixedRate(new LogRotationTask(), 1, 24, TimeUnit.HOURS);
}
private Properties loadApplicationConfig() {
Properties config = new Properties();
try (InputStream input = getClass().getResourceAsStream("/application.properties")) {
if (input != null) {
config.load(input);
}
} catch (IOException e) {
logger.warn("Could not load application.properties", e);
}
return config;
}
// Shutdown methods
private void stopBackgroundTasks(ServletContext context) {
ScheduledExecutorService scheduler =
(ScheduledExecutorService) context.getAttribute("app.scheduler");
if (scheduler != null && !scheduler.isShutdown()) {
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(30, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
}
}
}
private void shutdownDatabase(ServletContext context) {
DataSource dataSource = (DataSource) context.getAttribute("app.dataSource");
if (dataSource instanceof Closeable) {
try {
((Closeable) dataSource).close();
logger.info("Database connections closed");
} catch (IOException e) {
logger.error("Error closing database connections", e);
}
}
}
// Helper methods
private DataSource createDataSource() {
// Create and configure DataSource
return null; // Implementation specific
}
private CacheManager createCacheManager() {
// Create and configure cache manager
return null; // Implementation specific
}
private void performMaintenance() {
// Perform periodic maintenance tasks
}
private void shutdownExternalServices(ServletContext context) {
// Close external service connections
}
private void shutdownCaching(ServletContext context) {
// Shutdown cache manager
}
private void shutdownScheduler(ServletContext context) {
// Already handled in stopBackgroundTasks
}
private void clearApplicationAttributes(ServletContext context) {
// Clear non-essential attributes
List<String> attributesToClear = Arrays.asList(
"app.dataSource", "app.scheduler", "app.cacheManager", "app.httpClient"
);
for (String attribute : attributesToClear) {
context.removeAttribute(attribute);
}
}
// Background task implementations
private static class SessionCleanupTask implements Runnable {
@Override
public void run() {
// Cleanup expired sessions, temporary files, etc.
}
}
private static class LogRotationTask implements Runnable {
@Override
public void run() {
// Rotate application logs
}
}
}/**
* Listener that monitors HTTP session activity and user behavior
*/
@WebListener
public class SessionActivityMonitor implements HttpSessionListener,
HttpSessionAttributeListener,
HttpSessionActivationListener,
HttpSessionIdListener {
private static final Logger logger = LoggerFactory.getLogger(SessionActivityMonitor.class);
private static final AtomicLong activeSessions = new AtomicLong(0);
private static final Map<String, SessionInfo> sessionRegistry = new ConcurrentHashMap<>();
@Override
public void sessionCreated(HttpSessionEvent se) {
HttpSession session = se.getSession();
long sessionCount = activeSessions.incrementAndGet();
SessionInfo sessionInfo = new SessionInfo(
session.getId(),
session.getCreationTime(),
session.getMaxInactiveInterval()
);
sessionRegistry.put(session.getId(), sessionInfo);
logger.info("Session created: {} (Total active: {})", session.getId(), sessionCount);
// Store session metrics in servlet context
ServletContext context = session.getServletContext();
context.setAttribute("sessions.active.count", sessionCount);
context.setAttribute("sessions.total.created",
((Long) context.getAttribute("sessions.total.created")) + 1);
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
long sessionCount = activeSessions.decrementAndGet();
SessionInfo sessionInfo = sessionRegistry.remove(session.getId());
if (sessionInfo != null) {
long duration = System.currentTimeMillis() - sessionInfo.getCreationTime();
logger.info("Session destroyed: {} (Duration: {}ms, Total active: {})",
session.getId(), duration, sessionCount);
// Log session statistics
logSessionStatistics(sessionInfo, duration);
}
// Update servlet context metrics
ServletContext context = session.getServletContext();
context.setAttribute("sessions.active.count", sessionCount);
}
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
String sessionId = event.getSession().getId();
String attributeName = event.getName();
Object attributeValue = event.getValue();
logger.debug("Session attribute added: {} -> {} = {}",
sessionId, attributeName, attributeValue);
// Track user login
if ("authenticated".equals(attributeName) && Boolean.TRUE.equals(attributeValue)) {
String username = (String) event.getSession().getAttribute("username");
logUserLogin(sessionId, username);
}
// Update session info
SessionInfo sessionInfo = sessionRegistry.get(sessionId);
if (sessionInfo != null) {
sessionInfo.addAttribute(attributeName, attributeValue);
}
}
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
String sessionId = event.getSession().getId();
String attributeName = event.getName();
Object attributeValue = event.getValue();
logger.debug("Session attribute removed: {} -> {} (was: {})",
sessionId, attributeName, attributeValue);
// Track user logout
if ("authenticated".equals(attributeName)) {
String username = (String) attributeValue;
logUserLogout(sessionId, username);
}
// Update session info
SessionInfo sessionInfo = sessionRegistry.get(sessionId);
if (sessionInfo != null) {
sessionInfo.removeAttribute(attributeName);
}
}
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
String sessionId = event.getSession().getId();
String attributeName = event.getName();
Object oldValue = event.getValue();
Object newValue = event.getSession().getAttribute(attributeName);
logger.debug("Session attribute replaced: {} -> {} = {} (was: {})",
sessionId, attributeName, newValue, oldValue);
// Update session info
SessionInfo sessionInfo = sessionRegistry.get(sessionId);
if (sessionInfo != null) {
sessionInfo.updateAttribute(attributeName, newValue);
}
}
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
String sessionId = se.getSession().getId();
logger.debug("Session will passivate: {}", sessionId);
SessionInfo sessionInfo = sessionRegistry.get(sessionId);
if (sessionInfo != null) {
sessionInfo.setPassivated(true);
}
}
@Override
public void sessionDidActivate(HttpSessionEvent se) {
String sessionId = se.getSession().getId();
logger.debug("Session did activate: {}", sessionId);
SessionInfo sessionInfo = sessionRegistry.get(sessionId);
if (sessionInfo != null) {
sessionInfo.setPassivated(false);
}
}
@Override
public void sessionIdChanged(HttpSessionEvent event, String oldSessionId) {
HttpSession session = event.getSession();
String newSessionId = session.getId();
logger.info("Session ID changed: {} -> {}", oldSessionId, newSessionId);
// Update session registry
SessionInfo sessionInfo = sessionRegistry.remove(oldSessionId);
if (sessionInfo != null) {
sessionInfo.setSessionId(newSessionId);
sessionRegistry.put(newSessionId, sessionInfo);
}
}
private void logUserLogin(String sessionId, String username) {
logger.info("User login: {} (Session: {})", username, sessionId);
// Update login metrics
// In a real application, you might store this in a database
}
private void logUserLogout(String sessionId, String username) {
logger.info("User logout: {} (Session: {})", username, sessionId);
}
private void logSessionStatistics(SessionInfo sessionInfo, long duration) {
logger.info("Session statistics - ID: {}, Duration: {}ms, Attributes: {}, Passivated: {}",
sessionInfo.getSessionId(),
duration,
sessionInfo.getAttributeCount(),
sessionInfo.isPassivated());
}
// Helper class to track session information
private static class SessionInfo {
private String sessionId;
private final long creationTime;
private final int maxInactiveInterval;
private final Map<String, Object> attributes;
private boolean passivated;
public SessionInfo(String sessionId, long creationTime, int maxInactiveInterval) {
this.sessionId = sessionId;
this.creationTime = creationTime;
this.maxInactiveInterval = maxInactiveInterval;
this.attributes = new ConcurrentHashMap<>();
this.passivated = false;
}
public String getSessionId() { return sessionId; }
public void setSessionId(String sessionId) { this.sessionId = sessionId; }
public long getCreationTime() { return creationTime; }
public int getMaxInactiveInterval() { return maxInactiveInterval; }
public boolean isPassivated() { return passivated; }
public void setPassivated(boolean passivated) { this.passivated = passivated; }
public void addAttribute(String name, Object value) {
attributes.put(name, value);
}
public void removeAttribute(String name) {
attributes.remove(name);
}
public void updateAttribute(String name, Object value) {
attributes.put(name, value);
}
public int getAttributeCount() {
return attributes.size();
}
}
}/**
* Listener that monitors request performance and resource usage
*/
@WebListener
public class RequestPerformanceMonitor implements ServletRequestListener,
ServletRequestAttributeListener {
private static final Logger logger = LoggerFactory.getLogger(RequestPerformanceMonitor.class);
private static final String REQUEST_START_TIME = "request.startTime";
private static final AtomicLong requestCounter = new AtomicLong(0);
private static final AtomicLong activeRequests = new AtomicLong(0);
@Override
public void requestInitialized(ServletRequestEvent sre) {
long requestId = requestCounter.incrementAndGet();
long activeCount = activeRequests.incrementAndGet();
long startTime = System.currentTimeMillis();
ServletRequest request = sre.getServletRequest();
// Store request metadata
request.setAttribute("request.id", requestId);
request.setAttribute(REQUEST_START_TIME, startTime);
request.setAttribute("request.thread", Thread.currentThread().getName());
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
logger.debug("Request initialized: {} {} {} (ID: {}, Active: {})",
httpRequest.getMethod(),
httpRequest.getRequestURI(),
httpRequest.getProtocol(),
requestId,
activeCount);
// Log detailed request information for monitoring
logRequestDetails(httpRequest, requestId);
}
// Update servlet context metrics
ServletContext context = sre.getServletContext();
context.setAttribute("requests.active.count", activeCount);
context.setAttribute("requests.total.count", requestId);
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
ServletRequest request = sre.getServletRequest();
long activeCount = activeRequests.decrementAndGet();
Long requestId = (Long) request.getAttribute("request.id");
Long startTime = (Long) request.getAttribute(REQUEST_START_TIME);
if (requestId != null && startTime != null) {
long duration = System.currentTimeMillis() - startTime;
String threadName = (String) request.getAttribute("request.thread");
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
logger.debug("Request destroyed: {} {} (ID: {}, Duration: {}ms, Active: {})",
httpRequest.getMethod(),
httpRequest.getRequestURI(),
requestId,
duration,
activeCount);
// Log performance metrics
logPerformanceMetrics(httpRequest, requestId, duration, threadName);
// Check for slow requests
if (duration > 5000) { // 5 seconds threshold
logger.warn("Slow request detected: {} {} (Duration: {}ms, ID: {})",
httpRequest.getMethod(),
httpRequest.getRequestURI(),
duration,
requestId);
}
}
}
// Update servlet context metrics
ServletContext context = sre.getServletContext();
context.setAttribute("requests.active.count", activeCount);
}
@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
String attributeName = srae.getName();
Object attributeValue = srae.getValue();
// Log important attribute additions
if (isImportantAttribute(attributeName)) {
Long requestId = (Long) srae.getServletRequest().getAttribute("request.id");
logger.debug("Request attribute added: {} = {} (Request ID: {})",
attributeName, attributeValue, requestId);
}
}
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
String attributeName = srae.getName();
Object attributeValue = srae.getValue();
// Log important attribute removals
if (isImportantAttribute(attributeName)) {
Long requestId = (Long) srae.getServletRequest().getAttribute("request.id");
logger.debug("Request attribute removed: {} (was: {}) (Request ID: {})",
attributeName, attributeValue, requestId);
}
}
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
String attributeName = srae.getName();
Object oldValue = srae.getValue();
Object newValue = srae.getServletRequest().getAttribute(attributeName);
// Log important attribute replacements
if (isImportantAttribute(attributeName)) {
Long requestId = (Long) srae.getServletRequest().getAttribute("request.id");
logger.debug("Request attribute replaced: {} = {} (was: {}) (Request ID: {})",
attributeName, newValue, oldValue, requestId);
}
}
private void logRequestDetails(HttpServletRequest request, long requestId) {
StringBuilder details = new StringBuilder();
details.append("Request Details (ID: ").append(requestId).append(")\n");
details.append(" Method: ").append(request.getMethod()).append("\n");
details.append(" URI: ").append(request.getRequestURI()).append("\n");
details.append(" Query: ").append(request.getQueryString()).append("\n");
details.append(" Remote: ").append(request.getRemoteAddr()).append("\n");
details.append(" User-Agent: ").append(request.getHeader("User-Agent")).append("\n");
details.append(" Content-Type: ").append(request.getContentType()).append("\n");
details.append(" Content-Length: ").append(request.getContentLengthLong()).append("\n");
// Log session info if available
HttpSession session = request.getSession(false);
if (session != null) {
details.append(" Session: ").append(session.getId()).append("\n");
}
logger.trace(details.toString());
}
private void logPerformanceMetrics(HttpServletRequest request, long requestId,
long duration, String threadName) {
// In a real application, you might send these metrics to a monitoring system
StringBuilder metrics = new StringBuilder();
metrics.append("Performance Metrics (ID: ").append(requestId).append(")\n");
metrics.append(" Duration: ").append(duration).append("ms\n");
metrics.append(" Thread: ").append(threadName).append("\n");
metrics.append(" Method: ").append(request.getMethod()).append("\n");
metrics.append(" URI: ").append(request.getRequestURI()).append("\n");
// Memory usage
Runtime runtime = Runtime.getRuntime();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
metrics.append(" Memory Used: ").append(usedMemory / 1024 / 1024).append("MB\n");
logger.debug(metrics.toString());
// Store metrics for aggregation
storeMetrics(request.getMethod(), request.getRequestURI(), duration);
}
private boolean isImportantAttribute(String attributeName) {
// Define which attributes are important to log
return attributeName.startsWith("user.") ||
attributeName.startsWith("security.") ||
attributeName.startsWith("business.") ||
"authenticated".equals(attributeName) ||
"currentUser".equals(attributeName);
}
private void storeMetrics(String method, String uri, long duration) {
// In a real application, store these metrics in a database or monitoring system
// for analysis and alerting
}
}/**
* Example object that implements HttpSessionBindingListener and
* HttpSessionActivationListener to manage its own lifecycle
*/
public class UserProfile implements HttpSessionBindingListener,
HttpSessionActivationListener,
Serializable {
private static final Logger logger = LoggerFactory.getLogger(UserProfile.class);
private static final long serialVersionUID = 1L;
private String username;
private String email;
private Date lastLoginTime;
private Map<String, Object> preferences;
// Transient fields that will be reinitialized after activation
private transient DatabaseConnection dbConnection;
private transient CacheManager cacheManager;
public UserProfile(String username, String email) {
this.username = username;
this.email = email;
this.lastLoginTime = new Date();
this.preferences = new HashMap<>();
// Initialize transient resources
initializeResources();
}
@Override
public void valueBound(HttpSessionBindingEvent event) {
String sessionId = event.getSession().getId();
logger.info("UserProfile bound to session: {} (User: {})", sessionId, username);
// Record user login
recordUserActivity("LOGIN", sessionId);
// Initialize any session-specific resources
initializeSessionResources(event.getSession());
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
String sessionId = event.getSession().getId();
logger.info("UserProfile unbound from session: {} (User: {})", sessionId, username);
// Record user logout
recordUserActivity("LOGOUT", sessionId);
// Cleanup resources
cleanupResources();
}
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
String sessionId = se.getSession().getId();
logger.debug("UserProfile will passivate: {} (User: {})", sessionId, username);
// Save state before passivation
saveUserState();
// Release non-serializable resources
releaseTransientResources();
}
@Override
public void sessionDidActivate(HttpSessionEvent se) {
String sessionId = se.getSession().getId();
logger.debug("UserProfile did activate: {} (User: {})", sessionId, username);
// Reinitialize transient resources
initializeResources();
// Restore user state
restoreUserState();
}
public void updateLastActivity() {
this.lastLoginTime = new Date();
// Update activity in persistent storage
if (dbConnection != null) {
try {
dbConnection.updateLastActivity(username);
} catch (Exception e) {
logger.warn("Failed to update last activity for user: " + username, e);
}
}
}
public void setPreference(String key, Object value) {
preferences.put(key, value);
// Persist preference change
persistPreference(key, value);
}
public Object getPreference(String key) {
return preferences.get(key);
}
private void initializeResources() {
try {
// Initialize database connection
this.dbConnection = DatabaseConnectionFactory.createConnection();
// Initialize cache manager
this.cacheManager = CacheManagerFactory.getInstance();
logger.debug("Transient resources initialized for user: {}", username);
} catch (Exception e) {
logger.error("Failed to initialize resources for user: " + username, e);
}
}
private void initializeSessionResources(HttpSession session) {
// Set up session-specific resources
session.setAttribute("user.loginTime", lastLoginTime);
session.setAttribute("user.preferences.count", preferences.size());
}
private void releaseTransientResources() {
if (dbConnection != null) {
try {
dbConnection.close();
} catch (Exception e) {
logger.warn("Error closing database connection for user: " + username, e);
}
dbConnection = null;
}
cacheManager = null;
logger.debug("Transient resources released for user: {}", username);
}
private void cleanupResources() {
// Release all resources when object is unbound
releaseTransientResources();
// Clear preferences cache
if (cacheManager != null) {
cacheManager.evict("user.preferences." + username);
}
}
private void recordUserActivity(String activity, String sessionId) {
// Record user activity in audit log or analytics system
logger.info("User activity: {} - {} (Session: {})", username, activity, sessionId);
}
private void saveUserState() {
// Save current state to persistent storage before passivation
try {
if (dbConnection != null) {
dbConnection.saveUserPreferences(username, preferences);
dbConnection.updateLastActivity(username);
}
} catch (Exception e) {
logger.error("Failed to save user state for: " + username, e);
}
}
private void restoreUserState() {
// Restore state after activation
try {
if (dbConnection != null) {
Map<String, Object> savedPreferences = dbConnection.loadUserPreferences(username);
if (savedPreferences != null) {
preferences.putAll(savedPreferences);
}
}
} catch (Exception e) {
logger.error("Failed to restore user state for: " + username, e);
}
}
private void persistPreference(String key, Object value) {
// Persist individual preference changes
try {
if (dbConnection != null) {
dbConnection.saveUserPreference(username, key, value);
}
// Update cache
if (cacheManager != null) {
cacheManager.put("user.preference." + username + "." + key, value);
}
} catch (Exception e) {
logger.warn("Failed to persist preference {} for user: {}", key, username, e);
}
}
// Getters and setters
public String getUsername() { return username; }
public String getEmail() { return email; }
public Date getLastLoginTime() { return lastLoginTime; }
public Map<String, Object> getPreferences() { return new HashMap<>(preferences); }
// Mock helper classes (would be real implementations)
private static class DatabaseConnectionFactory {
static DatabaseConnection createConnection() { return new DatabaseConnection(); }
}
private static class DatabaseConnection {
void updateLastActivity(String username) throws Exception {}
void saveUserPreferences(String username, Map<String, Object> preferences) throws Exception {}
Map<String, Object> loadUserPreferences(String username) throws Exception { return null; }
void saveUserPreference(String username, String key, Object value) throws Exception {}
void close() throws Exception {}
}
private static class CacheManagerFactory {
static CacheManager getInstance() { return new CacheManager(); }
}
private static class CacheManager {
void evict(String key) {}
void put(String key, Object value) {}
}
}This comprehensive coverage of listeners and events provides all the tools needed for implementing sophisticated event-driven architectures and monitoring systems in servlet applications, enabling applications to respond intelligently to lifecycle changes and state modifications.
Install with Tessl CLI
npx tessl i tessl/maven-javax-servlet--javax-servlet-api