## Seguimiento de Sesión

El seguimiento de sesión es un mecanismo que los servlets utilizan para mantener el estado sobre la serie de peticiones desde un mismo usuario (esto es, peticiones originadas desde el mismo navegador) durante un periodo de tiempo.

Las sesiones son compartidas por los servlets a los que accede el cliente. Esto es conveniente para aplicaciones compuestas por varios servlets. Por ejemplo, Duke's Bookstore utiliza seguimiento de sesión para seguir la pista de los libros pedidos por el usuario. Todos los servlets del ejemplo tienen acceso a la sesión del usuario.

Para utilizar el seguimiento de sesión debemos:

- Obtener una sesión (un objeto HttpSession) para un usuario.

- Almacenar u obtener datos desde el objeto HttpSession.

- Invalidar la sesión (opcional).

### Obtener una Sesión

El método getSession del objeto HttpServletRequest devuelve una sesión de usuario. Cuando llamamos al método con su argumento create como true, la implementación creará una sesión si es necesario.

Para mantener la sesión apropiadamente, debemos lamar a getSession antes de escribir cualquier respuesta. (Si respondemos utilizando un Writer, entonces debemos llamar a getSession antes de acceder al Writer, no sólo antes de enviar cualquier respuesta).

El ejemplo Duke's Bookstore utiliza seguimiento de sesión para seguir la pista de los libros que hay en la hoja de pedido del usuario. Aquí tenemos un ejemplo de CatalogServlet obteniendo una sesión de usuario:

        public class CatalogServlet extends HttpServlet {

            public void doGet (HttpServletRequest request,
                               HttpServletResponse response)
        	throws ServletException, IOException
            {
                // Get the user's session and shopping cart
    	    HttpSession session = request.getSession(true);
                ...
    	    out = response.getWriter();
                ...
            }
        }

### Almacenar y Obtener Datos desde la Sesión

El Interface HttpSession proporciona métodos que almacenan y recuperan:

- Propiedades de Sesión Estándard, como un identificador de sesión.

- Datos de la aplicación, que son almacenados como parejas nombre-valor, donde el nombre es un string y los valores son objetos del lenguaje de programación Java. Como varios servlets pueden acceder a la sesión de usuario, deberemos adoptar una convención de nombrado para organizar los nombres con los datos de la aplicación. Esto evitará que los servlets sobreescriban accidentalmente otros valores de la sesión. Una de esas convenciones es servletname.name donde servletname es el nombre completo del servlet, incluyendo sus paquetes. Por ejemplo, com.acme.WidgetServlet.state es un cookie con el servletname com.acme.WidgetServlet y el name state.

El ejemplo Duke's Bookstore utiliza seguimiento de sesión para seguir la pista de los libros de la hoja de pedido del usuario. Aquí hay un ejemplo de CatalogServlet obteniendo un identificador de sesión de usuario, y obteniendo y seleccionando datos de la aplicación asociada con la sesión de usuario:

        public class CatalogServlet extends HttpServlet {

            public void doGet (HttpServletRequest request,
                               HttpServletResponse response)
        	throws ServletException, IOException
            {
                // Get the user's session and shopping cart
    	    HttpSession session = request.getSession(true);
    	    ShoppingCart cart =
                    (ShoppingCart)session.getValue(session.getId());

            // If the user has no cart, create a new one
            if (cart == null) {
                cart = new ShoppingCart();
                session.putValue(session.getId(), cart);
            }
                ...
            }
        }

Como un objeto puede ser asociado con una sesión, el ejemplo Duke's Bookstore sigue la pista de los libros que el usuario ha pedido dentro de un objeto. El tipo del objeto es ShoppingCart y cada libro que el usuario a seleccionado es almacenado en la hoja de pedidos como un objeto ShoppingCartItem. Por ejemplo, el siguiente código procede del método doGet de CatalogServlet:

        public void doGet (HttpServletRequest request,
                           HttpServletResponse response)
    	throws ServletException, IOException
        {
    	HttpSession session = request.getSession(true);
    	ShoppingCart cart = (ShoppingCart)session.getValue(session.getId());
            ...
            // Check for pending adds to the shopping cart
            String bookId = request.getParameter("Buy");

            //If the user wants to add a book, add it and print the result
            String bookToAdd = request.getParameter("Buy");
            if (bookToAdd != null) {
                BookDetails book = database.getBookDetails(bookToAdd);

                cart.add(bookToAdd, book);
                out.println("<p><h3>" + ...);
            }
        }

Finalmente, observa que una sesión puede ser designada como nueva. Una sesión nueva hace que el método isNew de la clase HttpSession devuelva true, indicando que, por ejemplo, el cliente, todavía no sabe nada de la sesión. Una nueva sesión no tiene datos asociados.

Podemos tratar con situaciones que involucran nuevas sesisones. En el ejemplo Duke's Bookstore, si el usuario no tiene hoja de pedido (el único dato asociado con una sesión), el servlet crea una nueva. Alternativamente, si necesitamos información sobre el usuario al iniciar una sesión (como el nombre de usuario), podríamos querer redireccionar al usuario a un "página de entrada" donde recolectamos la información necesaria.

### Invalidar la Sesión

Una sesión de usuario puede ser invalidada manual o automáticamente, dependiendo de donde se esté ejecutando el servlet. (Por ejemplo, el Java Web Server, invalida una sesión cuando no hay peticiones de página por un periodo de tiempo, unos 30 minutos por defecto). Invalidar una sesión significa eliminar el objeto HttpSession y todos sus valores del sistema.

Para invalidar manualmente una sesión, se utiliza el método invalidate de "session". Algunas aplicaciones tienen un punto natural en el que invalidar la sesión. El ejemplo Duke's Bookstore invalida una sesión de usuario después de que el usuario haya comprado los libros. Esto sucede en el ReceiptServlet:

        public class ReceiptServlet extends HttpServlet {

            public void doPost(HttpServletRequest request,
                               HttpServletResponse response)
    	    throws ServletException, IOException {

                    ...
                    scart = (ShoppingCart)session.getValue(session.getId());
                    ...
                    // Clear out shopping cart by invalidating the session
                    session.invalidate();

                    // set content type header before accessing the Writer
                    response.setContentType("text/html");
                    out = response.getWriter();
                    ...
            }
        }

### Manejar todos los Navegadores

Por defecto, el seguimiento de sesión utiliza cookies para asociar un identificador de sesión con un usuario. Para soportar también a los usuarios que acceden al servlet con un navegador que no soporta cookies, o si este está programado para no aceptarlas. debemos utilizar reescritura de URL en su lugar.

Cuando se utiliza la reescritura de URL se llama a los métodos que, cuando es necesario, incluyen el ID de sesión en un enlace. Debemos llamar a esos métodos por cada enlace en la respuesta del servlet.

El método que asocia un ID de sesión con una URL es HttpServletResponse.encodeUrl. Si redirecionamos al usuario a otra página, el método para asociar el ID de sesión con la URL redirecionada se llama HttpServletResponse.encodeRedirectUrl.

Los métodos encodeUrl y encodeRedirectUrl deciden si las URL necesitan ser reescritas, y devolver la URL cambiada o sin cambiar. (Las reglas para las URLs y las URLs redireccionadas son diferentes, pero en general si el servidor detecta que el navegador soporta cookies, entonces la URL no se reescribirá).

El ejemplo Duke's Bookstore utiliza reescritura de URL para todos los enlaces que devuelve a sus usuarios. Por ejemplo, el CatalogServlet devuelve un catalogo con dos enlaces para cada libro. Un enlace ofrece detalles sobre el libro y el otro ofrece al usuario añadir el libro a su hoja de pedidos. Ambas URLs son reescritas:

        public class CatalogServlet extends HttpServlet {

            public void doGet (HttpServletRequest request,
                               HttpServletResponse response)
    	    throws ServletException, IOException
            {
                // Get the user's session and shopping cart, the Writer, etc.
                ...
    	    // then write the data of the response
                out.println("<html>" + ...);
                ...
                // Get the catalog and send it, nicely formatted
                BookDetails[] books = database.getBooksSortedByTitle();
                ...
                for(int i=0; i < numBooks; i++) {
                    ...
                    //Print out info on each book in its own two rows
                    out.println("<tr>" + ...

                            "<a href=\"" +
                            response.encodeUrl("/servlet/bookdetails?bookId=" +
                                               bookId) +
                            "\"> <strong>" + books[i].getTitle() +
                            "  </strong></a></td>" + ...

                            "<a href=\"" +
                            response.encodeUrl("/servlet/catalog?Buy=" + bookId)
                            + "\">   Add to Cart  </a></td></tr>" +

                }
            }
        }

Si el usuario pulsa sobre un enlace con una URL re-escrita, el servlet reconoce y extrae el ID de sesión. Luego el método getSessionutiliza el ID de sesión para obtener el objeto HttpSession del usuario.

Por otro lado, si el navegador del usuario no soporta cookies y el usuario pulsa sobre una URL no re-escrita. Se pierde la sesión de usuario. El servlet contactado a través de ese enlace crea una nueva sesión, pero la nueva sesión no tiene datos asociados con la sesión anterior. Una vez que un servlet pierde los datos de una sesión, los datos se pierden para todos los servlets que comparten la sesión. Debemos utilizar la re-escritura de URLs consistentemente para que nuestro servlet soporte clientes que no soportan o aceptan cookies.

Ozito