Les raisons de ce petit projet
J’ai, dernièrement, eu un petit problème sur un serveur Websphere dont j’assure l’administration. Une page web (JSP) s’exécutant sur ce serveur d’application avait un bug (une boucle infinie). Cela a été relativement difficile à identifier car rien ne me permettait d’identifier les pages en cours d’exécution sur ce serveur.
Pour palier à ce manque d’informations j’ai réalisé un petit filtre qui me permet de suivre cela.
Je vous propose ce petit utilitaire à titre d’exemple de ServletFilter en espérant que cela pourra vous être utile.
Le principe du projet
Il consiste à placer dans une HashTable les pages en cours d’exécution. J’ai profité de cela pour ajouter certaines autres données me permettant d’offrir quelques statistiques supplémentaires (temps d’exécution, nombre d’erreurs, nombre d’exécutions,…). Une page jsp affiche le contenu de cette Hashtable.
L’utilisation d’un filtre me permet d’ajouter cette option à l’ensemble des webapps sans impacter le code existant (juste le fichier web.xml à modifier pour y définir le filtre).
Source du projet
L’ensemble du code source est disponible en téléchargement. L’application est composée de 3 classes très simples :
StatsFilter.java : Le filtre
Petite classe réalisant tout son travail dans la méthode doFilter
package com.berthou.web.filtre; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public final class StatsFilter implements Filter { private FilterConfig filterConfig = null; private String appli = "" ; public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (filterConfig == null) return; String key = "" ; long startTime = System.currentTimeMillis(); try { key = appli + ((HttpServletRequest)request).getServletPath() ; // On ajoute la page dans la liste des pages en cours MonitorJsp.action( MonitorJsp.ACT_ADD, key, 0) ; chain.doFilter(request, response); // On supprime la page dans la liste des pages en cours (+temsp d'execution) MonitorJsp.action( MonitorJsp.ACT_DEL, key, (System.currentTimeMillis() - startTime) ) ; } catch (IOException io) { // Une erreur ce produit => on la trace puis on la renvoie MonitorJsp.action( MonitorJsp.ACT_ERR, key, (System.currentTimeMillis() - startTime) ) ; throw io ; } catch (ServletException se) { // Une erreur ce produit => on la trace puis on la renvoie MonitorJsp.action( MonitorJsp.ACT_ERR, key, (System.currentTimeMillis() - startTime) ) ; throw se ; } } /** * Lecture des paramètres d'exécution (dans web.xml) * Ici uniquement la zone APPLICATION */ public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; appli = filterConfig.getInitParameter("APPLICATION") ; } public void destroy() { this.filterConfig = null; } }
MonitorJsp.java : Le Moniteur
C’est une classe statique de supervision (possible de passer a un singleton)
package com.berthou.web.filtre; import java.text.SimpleDateFormat; import java.util.*; import java.io.* ; /** * Classe de suppervision permettant de suivre l'état de certains objets * c'est un classe <b>static</b> * * @author rberthou * */ public class MonitorJsp { public static final String version = "MonitorJsp V1.00 du 4 Septembre 2008"; static protected Hashtable map = new Hashtable(100); static final int ACT_ADD = 1 ; static final int ACT_DEL = 2 ; static final int ACT_ERR = 5 ; public static synchronized void action(int act, String key, long delay) { ItemMonitor itm = (ItemMonitor)map.get(key) ; if ( itm == null ) { if (act == ACT_ADD) { itm = new ItemMonitor() ; map.put(key, itm) ; } } else { if (act == ACT_ADD) itm.add() ; else if (act == ACT_DEL) itm.remove(delay) ; else if (act == ACT_ERR) itm.error(delay) ; } } ..... [CUT] .... }
ItemMonitor.java : Les données
C’est une représentation d’un élément de la collection
package com.berthou.web.filtre; public class ItemMonitor { private long nbrun = 0 ; // Nombre d'execution private long nbact = 0 ; // Nombre de page active private long nberr = 0 ; // Nombre d'erreur private long mints = 0 ; // Temps min private long maxts = 0 ; // Temps Max private long lastAcc = 0 ; // date last exec /** * Constructeur */ public ItemMonitor() { this.add() ; } public void reset() { nbrun = 0 ; nberr = 0 ; mints = 0 ; maxts = 0 ; } public void add() { nbact++ ; lastAcc = System.currentTimeMillis() ; } public void remove(long delay) { nbrun++ ; nbact-- ; sumts += delay ; if (mints < 1 || delay < mints) mints = delay ; if (delay > maxts) maxts = delay ; } public void error(long delay) { nberr++ ; nbact-- ; } ..... [CUT] ..... }
Utilisation
Pour utiliser ce filtre il suffit de :
- Ajouter l’archive .jar dans les librairies partagées de votre serveur d’application. (Attention : si vous placez cela directement dans votre répertoire /WEB-INF/lib de votre webapp cela ne fonctionnera que pour cette application.)
- Modifier le fichier web.xml de pour y paramétrer ce filtre (exemple ci dessous).
<filter> <filter-name>MonacoFilter</filter-name> <filter-class>com.berthou.web.flitre.StatsFilter</filter-class> <init-param> <param-name>APPLICATION</param-name> <param-value>TEST1</param-value> </init-param> </filter> <filter-mapping> <filter-name>StatsFilter</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping>
Cela vous permet d’activer le filtre dans votre webapp il suffit alors d’écrire une page jsp de la forme suivante pour afficher les données mémorisées.
<%@ import="com.berthou.web.filtre.*,java.util.*"%> <% // Get iterator for keys in HashMap Hashtable monitorSession = (Hashtable)MonitorJsp.cloneIt("b"); Iterator sessionIter = monitorSession.keySet().iterator(); String id = "" ; ItemMonitor itm = null ; %> <table><thead><tr> <th>id</th><th>act</th><th>run</th><th>err</th><th>last</th><th>min/max</th><th>Sum</th> </tr></thead> <tbody><% while(sessionIter.hasNext() ) { id = (String)sessionIter.next(); itm = (ItemMonitor)monitorSession.get(id); try { %><tr valign="top"><td ><%=id%></td><%=itm.toHtmlString()%></tr><% } catch ( java.lang.IllegalStateException ie ) { } } %> </tbody></table>
Attention
J’utilise une classe statique qui est “synchronized”. Cela risque d’être un goulot d’étranglement pour les serveurs d’applications à fort trafic. Personnellement j’utilise cela en production dans le cadre d’un intranet sans aucun problème mais attention tout de même.
Téléchargements
StatsFilter.zip le code Source
StatsFilter.jar la librairie