Archives mensuelles : février 2014

JBoss/eXo Portal (Tomcat) : Valves et Filtres

Contexte :

Tomcat permet de d’intercepter les requêtes et les réponses. Cela peut s’avérer intéressant pour faire différents traitements tel que par exemple l’encodage des paramètres d’une requête (encodage systématique), la gestion propre d’un logout avec invalidation de la session… Les applications sont multiples.

Les Valves Tomcat :

La technologie des Valves est propre à Tomcat et n’existe pas forcement sur les autres serveurs d’application (En tout cas pas sous cette forme).

La Valve, lorsqu’elle est appelée, intercepte la requête Http avec et met a disposition les paramètres Request et Response de celle-ci. Elle étend la classe « org.apache.catalina.valves.ValveBase » et doit surcharger la méthode « invoke ».

On peut créer autant de Valves que l’on veut et celles-ci vont s’enchaîner lorsqu’on appelle la méthode « getNext().invoke ». Si cet appel est homis, aucune autre valve sera appelée.

Les Valves sont invoquées dans l’ordre de leurs déclaration dans le fichier Xml.


import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Invalidation des sessions
*/
public class LogoutValve extends ValveBase {
	private static final Logger LOGGER = LoggerFactory.getLogger(LogoutValve.class);

	@Override  public void invoke(Request pRequest, Response pResponse) throws IOException, ServletException {
		if (pRequest.getParameterMap().size() > 0
			&& pRequest.getParameterMap().containsKey("amp;portal:action")
			&& ((String[]) pRequest.getParameterMap().get("amp;portal:action"))[0].equals("Logout")) {
				// On invalide la session et on crée une nouvelle session
				LOGGER.info("Logout : Invlidation de la session !");
				pRequest.getSession(true).invalidate();
				pRequest.setRequestedSessionId(null);
				pRequest.clearCookies();

				// Create a new session and set it to the request
				Session newSession = pRequest.getSessionInternal(true);
				pRequest.setRequestedSessionId(newSession.getId());

				// Redirection vers l'accueil
				((HttpServletResponse) pResponse).sendRedirect(pRequest.getRequestURL().toString());
			} else {
				// Passage a la prochaine valve
				super.getNext().invoke(pRequest, pResponse);
			}
		}
	}
}

Paramétrage des Valves dans le portal.xml :

<Context path="/portal" docBase="portal" debug="0" reloadable="true" crossContext="true" privileged="true">
    <Valve className='org.toto.filtre.LogoutValve' characterEncoding='UTF-8' />
</context>

Javax.servlet.Filter

Les filtres du package « Javax » permettent un fonctionnement similaire. Lorsqu’on les paramètre dans le « web.xml » on leurs indique un « url-pattern » qui va définir sur quelle url le Filtre doit se déclencher. Du coup on peut, par rapport aux Valves, affiner leurs déclenchement et ne pas le rendre systématique.

Les filtres s’enchaînent aussi et on laisse la possibilité au développeur de continuer le chaînage des filtres ou d’intérompre celle-ci. Le 3e paramètre, FilterChain, permet donc de gérer la pile d’appel des filtres.

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

public class LogoutFilter implements Filter {
    @Override
    public void doFilter(ServletRequest pRequest, ServletResponse pResponse, FilterChain pFilterChain) throws IOException, ServletException {
        // On invalide la session et on crée une nouvelle session
        ((HttpServletRequest) pRequest).getSession().invalidate();
        ((HttpServletRequest) pRequest).getSession(true);
    }
}

Paramétrage des filtres dans le web.xml :


<filter>
    <filter-name>logoutFilter</filter-name>
    <filter-class>org.toto.web.LogoutFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>logoutFilter</filter-name>
    <url-pattern>/logout</url-pattern>
</filter-mapping>

Tomcat : Redirection des loggeurs vers LogBack

Le but est de rediriger l’ensemble des loggeurs d’un Tomcat vers LogBack.

Pour ce faire, il faut supprimer les librairies suivantes dans Tomcat/lib :
– commons-logging-1.1.1.jar
– log4j-1.2.1.4.jar
Les numéros de version sont la à titre indicatif mais peuvent varier en fonction des versions de Tomcat.

Ensuite, il faut remplacer les librairies par les bridges suivants :
– jcl-over-slf4j-1.5.8.jar
– jul-to-slf4j-1.5.8.jar
– log4j-over-slf4j-1.5.8.jar

Il faut rajouter les librairies LogBack (toujours dans Tomcat/lib) :
– logback-classic-0.9.17.jar
– logback-core-0.9.17.jar

Etape suivante, création d’un fichier de paramétrage de LogBack. Dans l’exemple on l’appelle « logback.xml » et on va le placer dans Tomcat/conf :

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
 <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator" />
 <contextName>exo-plateform</contextName>

 <jmxConfigurator />

 <appender name="FILE-APP" class="ch.qos.logback.core.FileAppender">
 <File>${catalina.home}/logs/logback.log</File>
 <layout class="ch.qos.logback.classic.PatternLayout">
 <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS,Europe/Paris} [%thread] %-5level %logger{36} - %msg %xEx%n</Pattern>
 </layout>
 </appender>

 <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
 <layout class="ch.qos.logback.classic.PatternLayout">
 <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS,Europe/Paris} [%thread] %-5level %logger{36} - %msg %xEx%n</Pattern>
 </layout>
 </appender>

 <root level="info">
 <appender-ref ref="FILE-APP" />
 <appender-ref ref="CONSOLE" />
 </root>
</configuration>

Dernière étape, il faut modifier le « catalina.properties » pour prendre en compte notre fichier de configuration de LogBack :

common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar,D://Java/applicatif/conf/langues,D://Java/applicatif/libs/*.jar,${catalina.home}/conf/logback

Pour éviter d’avoir des fichiers logs résiduels à 0ko, il faut supprimer le fichier « logging.properties » qui ne sera désormais plus utilisé.

Pour terminer, on peut rediriger aussi tous les logs Tomcat de base dans LogBack. Pour ça, il faut télécharger les librairies suivantes des « EXTRAS » de Tomcat :
– tomcat-juli.jar
– tomcat-juli-adapters.jar

Tomcat-Juli remplace la librairie initiale du même nom dans Tomcat/bin et les « adapters » vont dans Tomcat/lib. Cette librairie n’existe pas, donc n’est pas écrasée. Ensuite il faut rajouter dans le fichier logback.xml les loggeurs suivants :

<logger name="org.apache.catalina" level="info">
 <appender-ref ref="CATALINA" />
</logger>

<logger name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost]" level="info">
 <appender-ref ref="LOCALHOST" />
</logger>

<logger
 name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager]" level="info">
 <appender-ref ref="MANAGER" />
</logger>

<logger
 name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/admin]" level="info">
 <appender-ref ref="ADMIN" />
</logger>

<logger
 name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager]" level="info">
 <appender-ref ref="HOST-MANAGER" />
</logger>

Normalement, tout devrait être correctement redirigé.