Dans le développement d'applications web Java, les "listeners" (écouteurs) jouent un rôle fondamental pour intercepter et réagir à divers événements se produisant au sein de l'environneemnt du serveur web. Ces événements peuvent concerner le cycle de vie des objets fondamentaux comme les requêtes, les sessions et le contexte d'application, ainsi que les modifications de leurs attributs. En implémentant les interfaces d'écouteur appropriées, les développeurs peuvent exécuter une logique personnalisée à des moments spécifiques, permettant ainsi la gestion de tâches d'initialisation, de nettoyage, de journalisation ou de surveillance.
Le principe des écouteurs repose sur le modèle de conception "Observateur". Lorsqu'un événement est déclenché (par exemple, la création d'une nouvelle session), le conteneur web informe tous les écouteurs enregistrés pour ce type d'événement, qui peuvent alors exécuter les méthodes implémentées pour y répondre. Cela offre une approche puissante et découplée pour gérer les comportements réactifs dans une application web.
Catégories d'Interfaces d'Écouteurs
Les interfaces d'écouteurs sont regroupées en plusieurs catégories selon l'objet qu'elles surveillent et le type d'événement.
1. Écouteurs de Cycle de Vie des Objets Web
Ces écouteurs sont déclenchés lors de la création ou de la destruction des objets principaux du conteneur web :
ServletRequestListener: Surveille la création et la destruction des objetsServletRequest.HttpSessionListener: Surveille la création et la destruction des objetsHttpSession.ServletContextListener: Surveille le démarrage et l'arrêt du contexte d'application web (objetServletContext).
2. Écouteurs de Modifications d'Attributs
Ces écouteurs sont déclenchés lorsqu'un attribut est ajouté, modifié ou supprimé dans un objet donné :
ServletRequestAttributeListener: Surveille les changements d'attributs dans unServletRequest.HttpSessionAttributeListener: Surveille les changements d'attributs dans uneHttpSession.ServletContextAttributeListener: Surveille les changements d'attributs dans unServletContext.
3. Écouteurs Spécifiques à la Session
Ces écouteurs gèrent des événements plus granulaires liés aux sessions :
HttpSessionBindingListener: Permet à un objet de savoir quand il est lié ou délié à une session. L'objet lui-même implémente cette interface.HttpSessionActivationListener: Déclenché lorsqu'un objet de session est activé ou désactivé (sérialisé/désérialisé) lors de la migration de session.
Mise en Œuvre et Configuration des Écouteurs
Pour utiliser un écouteur, deux étapes principales sont nécessaires :
- Création de la Classe d'Écouteur : Écrivez une classe Java ordinaire qui implémente l'interface d'écouteur souhaitée et surcharge ses méthodes d'événement. C'est dans ces méthodes que votre logique personnalisée sera exécutée.
- Déclaration dans le Fichier de Déploiement : Enregistrez votre écouteur dans le fichier
web.xmlde votre application web. Pour les versions plus modernes de la spécification Servlet (3.0+), les annotations (par exemple,@WebListener) peuvent également être utilisées.
Exemples de Classes d'Écouteurs
Voici des exemples de classes implémentant les écouteurs de cycle de vie les plus courants. Les messages de journalisation sont utilisés pour illustrer quand chaque méthode est appelée.
Écouteur de Contexte d'Application (ServletContextListener)
Cet écouteur est utile pour exécuter des actions une seule fois au démarrage et à l'arrêt de l'application.
package fr.devtech.web.listeners;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.time.LocalDateTime;
public class GestionnaireContexte implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
event.getServletContext().setAttribute("dateDemarrageApplication", LocalDateTime.now());
System.out.println("INFO: L'application web a démarré à " + LocalDateTime.now());
System.out.println("INFO: Contexte de servlet initialisé. Nom : " + event.getServletContext().getServletContextName());
}
@Override
public void contextDestroyed(ServletContextEvent event) {
LocalDateTime demarrage = (LocalDateTime) event.getServletContext().getAttribute("dateDemarrageApplication");
if (demarrage != null) {
long uptimeSeconds = java.time.Duration.between(demarrage, LocalDateTime.now()).getSeconds();
System.out.println("INFO: L'application web s'est arrêtée après " + uptimeSeconds + " secondes de fonctionnement.");
} else {
System.out.println("INFO: L'application web s'est arrêtée à " + LocalDateTime.now());
}
System.out.println("INFO: Contexte de servlet détruit.");
}
}
Écouteur de Session (HttpSessionListener)
Cet écouteur permet de suivre la création et la destruction des sessions utilisateur, par exemple pour compter les utilisateurs actifs ou gérer les ressources liées à la session.
package fr.devtech.web.listeners;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicInteger;
public class GestionnaireSession implements HttpSessionListener {
private static final AtomicInteger sessionsActives = new AtomicInteger(0);
@Override
public void sessionCreated(HttpSessionEvent event) {
sessionsActives.incrementAndGet();
System.out.println("INFO: Nouvelle session créée (ID: " + event.getSession().getId() + ") à " + LocalDateTime.now() + ". Sessions actives: " + sessionsActives.get());
event.getSession().setAttribute("creationTime", LocalDateTime.now());
}
@Override
public void sessionDestroyed(HttpSessionEvent event) {
sessionsActives.decrementAndGet();
System.out.println("INFO: Session détruite (ID: " + event.getSession().getId() + ") à " + LocalDateTime.now() + ". Sessions actives: " + sessionsActives.get());
LocalDateTime creationTime = (LocalDateTime) event.getSession().getAttribute("creationTime");
if (creationTime != null) {
long durationSeconds = java.time.Duration.between(creationTime, LocalDateTime.now()).getSeconds();
System.out.println("INFO: La session a duré " + durationSeconds + " secondes.");
}
}
}
Écouteur de Requête (ServletRequestListener)
Cet écouteur permet d'effectuer des opérations au début et à la fin de chaque requête web, comme la journalisation des temps de réponse ou l'initialisation de contextes de threads.
package fr.devtech.web.listeners;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import java.time.LocalDateTime;
public class GestionnaireRequete implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent event) {
// Pour les versions récentes de Servlet API (6.0+), on pourrait utiliser event.getServletRequest().getRequestId();
String identifiantRequete = String.valueOf(event.getServletRequest().hashCode());
System.out.println("DEBUG: Requête [" + identifiantRequete + "] initialisée à " + LocalDateTime.now());
event.getServletRequest().setAttribute("heureDebutTraitement", LocalDateTime.now());
}
@Override
public void requestDestroyed(ServletRequestEvent event) {
String identifiantRequete = String.valueOf(event.getServletRequest().hashCode());
LocalDateTime heureDebut = (LocalDateTime) event.getServletRequest().getAttribute("heureDebutTraitement");
if (heureDebut != null) {
long dureeMs = java.time.Duration.between(heureDebut, LocalDateTime.now()).toMillis();
System.out.println("DEBUG: Requête [" + identifiantRequete + "] détruite après " + dureeMs + " ms de traitement.");
} else {
System.out.println("DEBUG: Requête [" + identifiantRequete + "] détruite.");
}
}
}
Configuration dans web.xml
Une fois les classes d'écouteurs implémentées, elles doivent être déclarées dans le descripteur de dépoliement de l'application web, web.xml, pour que le conteneur web puisse les instancier et les enregistrer.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- Déclaration des écouteurs personnalisés -->
<listener>
<listener-class>fr.devtech.web.listeners.GestionnaireContexte</listener-class>
</listener>
<listener>
<listener-class>fr.devtech.web.listeners.GestionnaireSession</listener-class>
</listener>
<listener>
<listener-class>fr.devtech.web.listeners.GestionnaireRequete</listener-class>
</listener>
</web-app>
Page JSP pour Déclencher les Événements
Pour observer le comportement de ces écouteurs, une simple page JSP peut déclencher des requêtes et manipuler des sessions, ce qui sera reflété dans les journaux du serveur.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<head>
<meta charset="UTF-8">
<title>Page de Test des Écouteurs</title>
</head>
<body>
<h1>Bienvenue sur la page de test des écouteurs !</h1>
<p>Cette page est conçue pour déclencher divers événements observés par les écouteurs de l'application.</p>
<%
// Définir un attribut dans l'objet request
request.setAttribute("utilisateurCourant", "Alice Durand");
out.println("<p>Attribut 'utilisateurCourant' défini dans la requête.</p>");
// Définir un attribut dans la session (peut être utile pour des écouteurs d'attributs de session)
session.setAttribute("languePreferee", "Français");
out.println("<p>Attribut 'languePreferee' défini dans la session.</p>");
// Invalider explicitement la session pour déclencher l'événement de destruction
// Cela forcera l'appel à la méthode sessionDestroyed() de l'écouteur de session.
session.invalidate();
out.println("<p>La session actuelle a été invalidée. Un nouvel accès créera une nouvelle session.</p>");
%>
<p>Vérifiez les journaux de votre serveur (console) pour voir les messages générés par les écouteurs.</p>
</body>
</html>