Hasta ahora sabemos que el contenedor web intercepta las peticiones y respuestas HTTP (no las peticiones EJB). Los filtros son el único mecanismo por el que podemos participar en el mecanismo de intercepción. Podemos introducir filtros que serán llamados antes de que se invoque a otros contenidos, como servlets, páginas html o jsp, etc.
El ciclo de vida es similar al de los servlets, también tienen un método init() por el que podemos conseguir un ServletContext, además tiene el método doFilter(), funcionalmente similar a doGet() o doPost, que recibe las invocaciones al filtro. Además se declaran en el descriptor web.xml de forma parecida. Los filtros pueden ser anidados, del mismo modo que un servlet puede invocar a otro.
Algunas utilidades de los filtros:
Hay una ventaja interesante: podemos introducir filtros declarativamente, en web.xml, sin tener que modificar los recursos que finalmente se invocan.
En el siguiente filtro vamos a contar las veces que se producen invocaciones a un determinado recurso. El recurso lo definiremos en web.xml. El contador lo almacenaremos como atributo del contexto. Lo que significa que el reinicio del contexto de aplicación, reiniciará el contador.
Al igual que un servlet el inicio del filtro supone una invocación a init(). doFilter() funciona de manera semejante a service() en un servlet. En este ejemplo puede observarse que el filtro debe implementar el interface Filter.
package docen_servlet01.filtro01;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletException;
import java.io.IOException;
/***********************************************************************************
* Filtro que cuenta las llamadas al recurso filtrado. La cuenta se almacena en
* atributo de contexto.
***********************************************************************************/
public class FiltroContador implements Filter {
FilterConfig config;
public void init(FilterConfig config) {
this.config = config;
}
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
throws IOException, ServletException {
ServletContext context = config.getServletContext();
//// Obtengo el contador del atributo del contexto
Integer contador = (Integer) context.getAttribute("contador.java.servlets.index");
//// El rearranque de contecto inicia contador
if (contador == null) {
contador = new Integer(0);
}
//// Incremento contador y guardo como atributo del contexto
contador = new Integer(contador.intValue() + 1);
context.setAttribute("contador.java.servlets.index", contador);
// Invoca al siguiente filtro. Si no lo hay continua con el recurso filtrado
chain.doFilter(request, response);
}
public void destroy() {}
}
Vamos a hacer un servlet que simplemente muestra el contador. Si el filtro no ha sido invocado, entonces devuelve el mensaje "contador no disponible":
package docen_servlet01.filtro01;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import docen_servlet01.JDBC01.presentacion.UtilGeneral;
/***********************************************************************************
* Este servlet muestra el contador del filtro, que esta almacenado
* en un atributo del contexto. El contador se reinicia cuando se reinicia el
* contexto de aplicación.
* Para las salidas HTML usamos docen_servlet01.JDBC01.presentacion.UtilGeneral
***********************************************************************************/
public class MostrarContador extends HttpServlet {
protected void doGet(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html; charset=iso-8859-1"); // Definir tipo de salida
PrintWriter out= response.getWriter(); // Obtener flujo salida;
try {
UtilGeneral.imprimirInicioPagina( "Ejemplo de servlet", "Muestra el contador usado por el filtro", out);
ServletContext context = getServletContext();
Integer contador = (Integer) context.getAttribute("contador.java.servlets.index");
if ( contador != null) {
UtilGeneral.imprimir( out, "Contador:" + contador.intValue());
}
else {
UtilGeneral.imprimir( out, "Contador no disponible" );
}
}
catch (Exception e) {
UtilGeneral.imprimir( out, "Excepción: " + e.getMessage() );
}
UtilGeneral.imprimirFinPagina(out);
}
}
En web.xml vamos a indicar el recurso que interceptamos. La forma en la que indicamos el filtro en web.xml es semejante a un servlet. Póngalos antes de la declaración de servlets.
<filter>
<filter-name>contador</filter-name>
<display-name>contador</display-name>
<filter-class>docen_servlet01.filtro01.FiltroContador</filter-class>
</filter>
<filter-mapping>
<filter-name>contador</filter-name>
<url-pattern>/java/servlets/index.html</url-pattern>
</filter-mapping>
Como era de esperar, filter-class hace referencia a la ruta física de la clase. Hay una diferencia respecto a los servlets: url-pattern en un servlet es la ruta lógica para invocarlo; en un filtro es la ruta lógica del recurso filtrado o interceptado. El filtro será invocado inmediatamente antes de que la petición llegue al recurso. En el ejemplo hemos puesto un archivo html (/java/servlets/index.html), pero podriamos poner cualquier archivo html (ejemplo: /java/servlets/*.html), también podriamos poner un servlet (por ejemplo, /servlet/FormClientes). Observe que no hace falta anteponer el contexto de aplicación (no sería /public_html/java/servlets/index.html). Si anida filtros procure ponerlos en web.xml en el orden en que se anidan.