Servelts y JSP
Seguimiento de Sesión
- ¿Qué es el Seguimiento de Sesión?
- El API Session Tracking
- Ejemplo: Mostrar Información de Sesión
Hay un número de problemas que vienen del hecho de que HTTP es un protocolo "sin estado". En particular, cuando estamos haciendo una compra on-line, es una molestia real que el servidor Web no puede recordar fácilmente transaciones anteriores. Esto hace que las aplicaciones como las cartas de compras sean muy problemáticas: cuando añadimos una entrada en nuestra carta, ¿cómo sabe el servidor que es realmente nuestra carta? Incluso si los servidores no retienen información contextual, todavía tendríamos problemas con comercio electrónico. Cuando nos movemos desde la página donde hemos especificado que queremos comprar (almacenada en un servidor Web normal) a la página que toma nuestro número de la tarjeta de crédito y la dirección de envío (almacenada en un servidor seguro que usa SSL), ¿cómo recuerda el servidor lo que hemos comprado?
Existen tres soluciones típicas a este problema:
- Cookies. Podemos usar cookies HTTP para almacenar información sobre una sesión de compra, y cada conexión subsecuente puede buscar la sesión actual y luego extraer la información sobre esa sesión desde una localización en la máquina del servidor. Esta es una excelente alternativa, y es la aproximación más ampliamente utilizada. Sin embargo, aunque los servlets tienen un Interface de alto nivel para usar cookies, existen unos tediosos detalles que necesitan ser controlados:
- Extraer el cookie que almacena el identificador de sesión desde los otros cookies (puede haber muchos, depués de todo),
- Seleccionar un tiempo de expiración apropiado para el cookie (las sesiones interrumpidas durante 24 horas probablemente deberían ser reseteadas), y
- Asociar la información en el servidor con el identificador de sesión (podría haber demasiada información que se almacena en el cookie, pero los datos sensibles como los números de las tarjetas de crédito nunca deben ir en cookies).
- Reescribir la URL. Podemos añadir alguna información extra al final de cada URL que identifique la sesión, y el servidor puede asociar ese identificador de sesión con los datos que ha almacenado sobre la sesión. Esta también es una excelente solución, e incluso tiene la ventaja que funciona con navegadores que no soportan cookies o cuando el usuario las ha desactivado. Sin embargo, tiene casi los mismos problemas que los cookies, a saber, que los programas del lado del servidor tienen mucho proceso que hacer, pero tedioso. Además tenemos que ser muy cuidadosos con que cada URL que le devolvamos al usuario tiene añadida la información extra. Y si el usuario deja la sesión y vuelve mediante un bookmark o un enlace, la información de sesión puede perderse.
- Campos de formulario ocultos. Los formularios HTML tienen una entrada que se parece a esto: <INPUT TYPE="HIDDEN" NAME="session" VALUE="...">. Esto significa que, cuando el formulario se envíe, el nombre y el valor especificado se incluiran en los datos GET o POST. Esto puede usarse para almacenar información sobre la sesión. Sin embargo, tiene la mayor desventaja en que sólo funciona si cada página se genera dinámicamente, ya que el punto negro es que cada sesión tiene un único identificador.
Los servlets proporcionan una solución técnica. Al API
HttpSession. Este es un interface de alto nivel construido sobre las cookies y la reescritura de URL. De hecho, muchos servidores, usan cookies si el navegador las soporta, pero automáticamente se convierten a reescritura de URL cuando las cookies no son soportadas o están desactivadas. Pero el autor de servlets no necesita molestarse con muchos detalles, no tiene que manipular explícitamente las cookies o la información añadida a la URL, y se les da automáticamente un lugar conveniente para almacenar los datos asociados con cada sesión.
Usar sesiones en servlets es bastante sencillo, envolver la búsqueda del objeto sesión asociado con la petición actual, crear un nuevo objeto sesión cuando sea necesario, buscar la información asociada con una sesión, almacenar la información de una sesión, y descartar las sesiones completas o abandonadas.
2.1 Buscar el objeto HttpSession asociado con la petición actual.
Esto se hace llamando al método
getSession de
HttpServletRequest. Si devuelve
null, podemos crear una nueva sesión, pero es tan comunmente usado que hay una opción que crea automáticamente una nueva sesión si no existe una ya. Sólo pasamos
true a
getSession. Así, nuestro primer paso normalmente se parecerá a esto:
HttpSession session = request.getSession(true);
2.2 Buscar la Información Asociada con un Sesión.
Los objetos
HttpSession viven en el servidor; son asociados automáticamente con el peticionario mediante un mecanismo detrás de la escena como los cookies o la reescritura de URL. Estos objetos sesión tienen una estructura de datos interna que nos permite almacenar un número de claves y valores asocidados. En la versión 2.1 y anteriores del API servlet, usamos
getValue("key") para buscar un valor préviamente almacenado. El tipo de retorno es
Object, por eso tenemos que forzarlo a un tipo más específico de datos. El valor de retorno es
null si no existe dicho atributo. En la versión 2.2
getValue está obsoleto en favor de
getAttribute, por el mejor nombrado correspondiente con
setAttribute (el correspondiente para
getValue es
putValue, no
setValue), y porque
setAttribute nos permite usar un
HttpSessionBindingListener asociado para monitorizar los valores, mientras que
putValue no. Aquí tenemos un ejemplo representativo, asumiendo que
ShoppingCart es alguna clase que hemos definido nosotros mismos y que almacena infomación de ítems para su venta
HttpSession session = request.getSession(true);
ShoppingCart previousItems =
(ShoppingCart)session.getValue("previousItems");
if (previousItems != null) {
doSomethingWith(previousItems);
} else {
previousItems = new ShoppingCart(...);
doSomethingElseWith(previousItems);
}
En la mayoría de los casos, tenemos un nombre atributo específico en mente, y queremos encontrar el valor (si existe) ya asociado con él. Sin embargo, también podemos descubrir todos los nombres de atributos en una sesión dada llamando a
getValueNames, que devuelve un array de
String. La versión 2.2, usa
getAttributeNames, que tienen un nombre mejor y que es más consistente ya que devuelve una
Enumeration, al igual que los métodos
getHeaders y
getParameterNames de
HttpServletRequest.
Aunque los datos que fueron asociados explícitamente con una sesión son la parte en la que debemos tener más cuidado, hay otras partes de información que son muy útiles también.
- getId. Este método devuelve un identificador único generado para cada sesión. Algunas veces es usado como el nombre clave cuando hay un sólo valor asociado con una sesión, o cuando se uso la información de logging en sesiones anteriores.
- isNew. Esto devuelve true si el cliente (navegador) nunca ha visto la sesión, normalmente porque acaba de ser creada en vez de empezar una referencia a un petición de cliente entrante. Devuelve false para sesión preexistentes.
- getCreationTime. Devuelve la hora, en milisegundos desde 1970, en la que se creo la sesión. Para obtener un valor útil para impresión, pasamos el valor al constructor de Date o al método setTimeInMillis de GregorianCalendar.
- getLastAccessedTime. Esto devuelve la hora, en milisegundos desde 1970, en que la seción fue enviada por última vez al cliente.
- getMaxInactiveInterval. Devuelve la cantidad de tiempo, en segundos, que la sesión debería seguir sin accesos antes de ser invalidada automáticamente. Un valor negativo indica que la sesión nunca se debe desactivar.
2.3 Asociar Información con una Sesión
Cómo se describió en la sección anterior, leemos la información asociada con una sesión usando
getValue (o
getAttribute en la versión 2.2 de las especificacioens Servlets). Observa que
putValue reemplaza cualquier valor anterior. Algunas veces esto será lo que queremos pero otras veces querremos recuperar un valor anterior y aumentarlo. Aquí tenemos un ejemplo:
HttpSession session = request.getSession(true);
session.putValue("referringPage", request.getHeader("Referer"));
ShoppingCart previousItems =
(ShoppingCart)session.getValue("previousItems");
if (previousItems == null) {
previousItems = new ShoppingCart(...);
}
String itemID = request.getParameter("itemID");
previousItems.addEntry(Catalog.getEntry(itemID));
// You still have to do putValue, not just modify the cart, since
// the cart may be new and thus not already stored in the session.
session.putValue("previousItems", previousItems);
Aquí tenemos un sencillo ejemplo que genera una página Web mostrando alguna información sobre la sesión actual. También puedes
descargar el código fuente.
package hall;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;
import java.util.*;
/** Simple example of session tracking. See the shopping
* cart example for a more detailed one.
* <P>
* Part of tutorial on servlets and JSP that appears at
* http://www.apl.jhu.edu/~hall/java/Servlet-Tutorial/
* 1999 Marty Hall; may be freely used or adapted.
*/
public class ShowSession extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession(true);
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Searching the Web";
String heading;
Integer accessCount = new Integer(0);;
if (session.isNew()) {
heading = "Welcome, Newcomer";
} else {
heading = "Welcome Back";
Integer oldAccessCount =
// Use getAttribute, not getValue, in version
// 2.2 of servlet API.
(Integer)session.getValue("accessCount");
if (oldAccessCount != null) {
accessCount =
new Integer(oldAccessCount.intValue() + 1);
}
}
// Use putAttribute in version 2.2 of servlet API.
session.putValue("accessCount", accessCount);
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + heading + "</H1>\n" +
"<H2>Information on Your Session:</H2>\n" +
"<TABLE BORDER=1 ALIGN=CENTER>\n" +
"<TR BGCOLOR=\"#FFAD00\">\n" +
" <TH>Info Type<TH>Value\n" +
"<TR>\n" +
" <TD>ID\n" +
" <TD>" + session.getId() + "\n" +
"<TR>\n" +
" <TD>Creation Time\n" +
" <TD>" + new Date(session.getCreationTime()) + "\n" +
"<TR>\n" +
" <TD>Time of Last Access\n" +
" <TD>" + new Date(session.getLastAccessedTime()) + "\n" +
"<TR>\n" +
" <TD>Number of Previous Accesses\n" +
" <TD>" + accessCount + "\n" +
"</TABLE>\n" +
"</BODY></HTML>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
Aquí tenemos un resultado típico, después de visitar la página varias veces sin salir del navegador entre medias:
Ozito