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.
�2. El API de Seguimiento de 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 sesi�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 queremos 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);
�3. Ejemplo: Mostrar Informaci�n de Sesi�n
Aqu� tenemos un sencillo ejemplo que genera una p�gina Web mostrando alguna informaci�n sobre la sesi�n actual. Tambi�n puedes 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:
