CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-javax-servlet--javax-servlet-api

Java Servlet API specification defining core interfaces and classes for web application development

Pending
Overview
Eval results
Files

listeners-events.mddocs/

Listeners and Events

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.

ServletContext Listeners

ServletContextListener Interface

/**
 * 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);
}

ServletContextAttributeListener Interface

/**
 * 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);
}

ServletContext Event Classes

/**
 * 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;
    }
}

ServletRequest Listeners

ServletRequestListener Interface

/**
 * 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);
}

ServletRequestAttributeListener Interface

/**
 * 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);
}

ServletRequest Event Classes

/**
 * 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;
    }
}

HTTP Session Listeners

HttpSessionListener Interface

/**
 * 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);
}

HttpSessionAttributeListener Interface

/**
 * 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);
}

HttpSessionActivationListener Interface

/**
 * 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);
}

HttpSessionBindingListener Interface

/**
 * 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);
}

HttpSessionIdListener Interface

/**
 * 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);
}

HTTP Session Event Classes

/**
 * 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;
    }
}

Listener Implementation Examples

Application Lifecycle Manager

/**
 * 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
        }
    }
}

Session Activity Monitor

/**
 * 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();
        }
    }
}

Request Performance Monitor

/**
 * 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
    }
}

Smart Attribute Binding Object

/**
 * 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

docs

async-processing.md

http-processing.md

index.md

listeners-events.md

security-filtering.md

servlet-lifecycle.md

session-cookies.md

tile.json