El seguimiento de sesiones es un mecanismo empleado por los servlets para gestionar un estado sobre las peticiones realizadas desde un mismo cliente (un mismo navegador) a lo largo de un período de tiempo determinado. Las sesiones se comparten por los servlets a los que accede un cliente (algo útil si queremos construir una aplicación basada en múltiples servlets).
Para utilizar el seguimiento de sesiones se tienen los pasos:
El método getSession() del objeto HttpServletRequest obtiene una sesión de usuario.
public HttpSession getSession() public HttpSession getSession(boolean crear)
El primer método obtiene la sesión actual, o crea una si no existe. Con el segundo método podemos establecer, mediante el flag booleano, si queremos crear una nueva si no existe (true) o no (false). Si la sesión es nueva, el método isNew() del HttpSession devuelve true, y la sesión no tiene ningún dato asociado. Deberemos ir añadiéndole datos tras crearla.
Para mantener la sesión de forma adecuada, debemos llamar a getSession() antes de que se escriba nada en la respuesta HttpServletResponse (y si utilizamos un Writer, debemos obtenerla antes de obtener el Writer, no antes de escribir datos).
Por ejemplo:
public class MiServlet extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession sesion = request.getSession(true); ... PrintWriter out = response.getWriter(); ... } }
La interfaz HttpSession proporciona métodos que permiten almacenar y obtener:
public String getId()
public Object getAttribute(String nombre) public void setAttribute(String nombre, Object valor) public void removeAttribute(String nombre)que obtienen / establecen / eliminan, respectivamente, valores de atributos. Estos métodos eran getValue(), putValue() y removeValue() hasta la versión 2.2 de servlets. Se tienen además métodos como getAttributeNames() para obtener los nombres de los atributos que se tienen almacenados para la sesión, y otros métodos de utilidad en la clase HttpSession.
Por ejemplo:
public class MiServlet extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession sesion = request.getSession(true); String nombreUsuario = (String)(sesion.getAttribute("nombre")); sesion.setAttribute("password", "secreto"); } }
El ejemplo lee el atributo "nombre" de la sesión, y establece el atributo "password" al valor "secreto".
Una sesión de usuario puede invalidarse manualmente, o automáticamente (dependiendo de dónde esté ejecutando el servlet). Esto implica eliminar el objeto HttpSession y sus valores de la memoria. Se tienen los métodos de HttpSession:
public int getMaxInactiveInterval() public void invalidate()
Para invalidarla automáticamente, la sesión expira cuando transcurre el tiempo indicado por el método getMaxInactiveInterval() entre dos accesos del cliente (en segundos)
Para invalidar manualmente una sesión, se emplea el método invalidate() de la misma. Esto puede ser interesante por ejemplo en comercio electrónico: podemos mantener una sesión donde se vayan acumulando los productos que un usuario quiera comprar, e invalidar la sesión (borrarla) cuando el usuario compre los productos.
Por ejemplo:
public class MiServlet extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession sesion = request.getSession(true); ... sesion.invalidate(); ... } }
Una alternativa para el seguimiento de sesiones consiste en la reescritura de URLs. Con esta técnica, se añaden como parámetros de la URL los datos relativos a la sesión actual, de forma que se van conservando entre las páginas.
El seguimiento de sesiones por defecto emplea cookies para almacenar el identificador de una sesión. Para poder utilizar seguimiento de sesiones con usuarios que accedan desde navegadores que no utilicen cookies, los servlets aplican automáticamente la reescritura de URLs cuando no se utilizan cookies.
Para poder utilizar esto, debemos codificar todas las URLs que referenciemos. Para esto se emplean los métodos:
public String encodeURL(String url) public String encodeRedirectURL(String url)
El primero se emplea para asociar identificadores con URLs, se utilizará cuando pongamos urls en el contenido de la página que generamos. El segundo se utiliza para asociar identificadores con redirecciones. Lo emplearemos cuando utilicemos métodos sendRedirect(), para codificar la URL que se le pasa. Ambos devuelven la URL sobreescrita si la sobreescritura ha sido necesaria, o la misma URL si no ha sido necesario sobreescribir.
Por ejemplo:
public class MiServlet extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... String url = response.encodeURL( "http://localhost:8080/mipagina.html"); out.println ("<a href=\"" + url + "\">...</a>"); ... String url2 = response.encodeRedirectURL( "http://otrapagina.com"); response.sendRedirect(url2); } }
Existen tres tipos de oyentes (listeners) que podemos utilizar en sesiones:
public void sessionCreated(HttpSessionEvent e) public void sessionDestroyed(HttpSessionEvent e)El objeto que implemente esta interfaz ejecutará el código de sessionCreated() cuando se inicie la sesión, y el de sessionDestroyed() cuando se termine. Las clases que implementen este oyente deben configurarse en el fichero descriptor de la aplicación, mediante etiquetas <listener>:
<listener> <listener-class>clase</listener-class> </listener>donde en <listener-class> se pone el nombre (completo) de la clase que implementa el listener. Así, el listener se registra, y el servidor ya sabe que tiene que notificarle en los momentos oportunos. Veremos ejemplos de uso de estos objetos con los oyentes de contexto (ServletContextListener), más adelante.
public void valueBound(HttpSessionBindingEvent e) public void valueUnbound(HttpSessionBindingEvent e)El objeto que implemente esta interfaz ejecutará el código de valueBound() cuando la sesión lo añada, y el método valueUnbound() cuando la sesión lo elimine.
public void sessionDidActivate(HttpSessionEvent e) public void sessionWillPassivate(HttpSessionEvent e)El objeto que implemente esta interfaz ejecutará el código de sessionDidActivate() cuando la sesión se active, y sessionWillPassivate() cuando se vuelva pasiva.
Aquí tenéis un WAR con un ejemplo de uso de sesiones. El servlet ServletSesiones cuenta el número de visitas a una página en una sola sesión (en una sola ventana de navegador).
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class ServletSesiones extends HttpServlet { // Metodo para GET public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); response.setHeader("Cache-Control", "no-cache"); HttpSession sesion = request.getSession(); PrintWriter out = response.getWriter(); if (sesion.isNew()) { // Mostramos un mensaje de primera visita out.println ("<HTML>"); out.println ("<BODY>"); out.println ("Primera visita a la pagina"); out.println ("<BR>"); out.println ("</BODY>"); out.println ("</HTML>"); sesion.setAttribute("contador", new Integer(1)); } else { // Mostramos el numero de visitas // y actualizamos el contador int contador = ((Integer) (sesion.getAttribute("contador"))).intValue(); contador++; out.println ("<HTML>"); out.println ("<BODY>"); out.println ("Visita numero " + contador + " a la pagina en esta sesion"); out.println ("<BR>"); out.println ("</BODY>"); out.println ("</HTML>"); sesion.setAttribute("contador", new Integer(contador)); } } }
Copiad el ejemplo en el directorio webapps de Tomcat, reiniciar el servidor y probadlo con:
http://localhost:8080/ejemplosesiones/servlet/ServletSesiones
El siguiente servlet utiliza como atributo de sesión una clase interna ObjetoSesion, que implementa la interfaz HttpSessionBindingListener. Dicho objeto tiene dentro un valor entero, y una cadena. Cada vez que el objeto se añade a la sesión se modifica un mensaje de texto, mostrando el valor entero actual del objeto:
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class EjemploServletListener extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); response.setHeader("Cache-Control", "no-cache"); HttpSession sesion = request.getSession(); PrintWriter out = response.getWriter(); if (sesion.isNew()) { // Mostramos mensaje de inicio out.println ("<HTML><BODY>" + "Mensaje de inicio" + "</BODY></HTML>"); sesion.setAttribute("contador", new ObjetoSesion(1)); } else { // Mostramos el valor actual del // objeto "contador" int contador = ((ObjetoSesion) (sesion.getAttribute("contador"))).getValor(); String mensaje = ((ObjetoSesion) (sesion.getAttribute("contador"))).getEnlazado(); out.println ("<HTML><BODY>"); out.println ("Valor: " + contador + "<br>Enlazado: " + mensaje); out.println ("</BODY></HTML>"); sesion.setAttribute("contador", new ObjetoSesion(contador+1)); } } } class ObjetoSesion implements HttpSessionBindingListener { int valor; String enlazado = "NO"; public ObjetoSesion(int valor) { this.valor = valor; } public void valueBound(HttpSessionBindingEvent e) { enlazado = "Objeto enlazado a la sesion " + valor + " veces"; } public void valueUnbound(HttpSessionBindingEvent e) { } public String getEnlazado() { return enlazado; } public int getValor() { return valor; } }
Si cargamos el servlet por primera vez en la sesión, muestra el mensaje:
Mensaje de bienvenida
Luego, cada vez que recarguemos el servlet se entrará en el bloque else, y al llamar al método setAttribute() se disparará el método valueBound(), actualizando la cadena. Con esto, con cada recarga se mostrará el mensaje:
Valor: i Enlazado: Objeto enlazado a la sesion i veces
siendo i el número de veces que se ha enlazado (que coincide con el número de veces que se ha cargado el servlet, en este caso).