4. Contexto global de la aplicación web

Vamos a estudiar los elementos que podemos utilizar para establecer una comunicación entre los distintos componentes de una aplicación web, tanto entre distintos servlets como entre servlets y otros elementos de la aplicación.

4.1. Contexto de los servlets

Comenzaremos estudiando el contexto de los servlets (Servlet Context). Este objeto de contexto es propio de cada aplicación web, es decir, tendremos un objeto ServletContext por aplicación web, por lo que nos servirá para comunicar los servlets de dicha aplicación.

public void init(ServletConfig config)

En la inicialización del servlet (método init), se nos proporcionará un objeto ServletConfig como parámetro. Mediante este objeto podemos:

  • Obtener el nombre del servlet, que figurará en el descriptor de despliegue de la aplicación web (web.xml en Tomcat).

    String nombre = config.getServletName();
  • Obtener los valores de los parámetros de inicialización del servlet, que se hayan establecido en el descriptor de despliegue. Tanto los nombres como los valores de los parámetros son cadenas de texto (String).

    String valor_param = config.getInitParameter(nombre_param);
    Enumeration nombres_params = config.getInitParameterNames();
  • Acceder al objeto de contexto de la aplicación a la que pertenece el servlet.

    ServletContext context = config.getServletContext();

    Esta última función es la más importante, ya que nos permite acceder al objeto de contexto global de la aplicación, con el que podremos realizar una serie de operaciones que veremos a continuación.

Contexto global de los servlets

Tanto el objeto ServletConfig como ServletContext pueden ser obtenidos directamente desde dentro de nuestro servlet llamando a los métodos getServletConfig y getServletContext respectivamente, definidos en GenericServlet, y por lo tanto disponibles en cualquier servlet.

4.1.1. Atributos de contexto

Dentro del objeto de contexto de nuestra aplicación podremos establecer una serie de atributos, que serán globales dentro de ella. Estos atributos son un conjunto de pares <nombre, valor> que podemos establecer y consultar desde los distintos servlets de nuestra aplicación web. El nombre del atributo será una cadena de texto (String), mientras que el valor podrá ser cualquier objeto java (Object).

Para consultar el valor de un atributo utilizaremos:

Object valor = context.getAttribute(nombre);

Daremos valor a un atributo con:

context.setAttribute(nombre, valor);

Podemos también eliminar un atributo:

context.removeAttribute(nombre);

Lo cual dejará el atributo a null, igual que si nunca le hubiesemos asignado un valor. Por último, con

Enumeration enum = context.getAttributeNames();

Obtenemos la lista de nombres de atributos definidos en el contexto.

Hay que hacer notar en este punto, que el objeto de contexto a parte de ser propio de cada aplicación web, es propio de cada máquina virtual Java. Cuando trabajemos en un contexto distribuido, cada máquina ejecutará una VM distinta, por lo que tendrán también objetos de contexto diferentes. Esto hará que si los servlets de una aplicación se alojan en máquinas distintas, tendrán contextos distintos y este objeto ya no nos servirá para comunicarnos entre ellos. Veremos más adelante formas alternativas de comunicación para estos casos.

4.1.2. Parámetros de inicialización

El objeto ServletConfig nos proporcionaba acceso a los parámetros de inicialización del servlet en el que nos encontramos. Con ServletContext, tendremos acceso a los parámetros de inicialización globales de nuestra aplicación web. Los métodos para obtener dichos parámetros son análogos a los que usabamos en ServletConfig:

String valor_param = context.getInitParameter(nombre_param);
Enumeration nombres_params = context.getInitParameterNames();

4.1.3. Acceso a recursos estáticos

Este objeto nos permite además acceder a recursos estáticos alojados en nuestro sitio web. Utilizaremos los métodos:

URL url = context.getResource(nombre_recurso);
InputStream in = context.getResourceAsStream(nombre_recurso);

El nombre del recurso que proporcionamos será una cadena que comience por "/", lo cual indica el directorio raiz dentro del contexto de nuestra aplicación, por lo tanto serán direcciones relativas a la ruta de nuestra aplicación web.

El primer método nos devuelve la URL de dicho recurso, mientras que el segundo nos devuelve directamente un flujo de entrada para leer dicho recurso.

Hay que señalar que esto nos permitirá leer cualquier recurso de nuestra aplicación como estático. Es decir, si proporcionamos como recurso "/index.jsp", lo que hará será leer el código fuente del JSP, no se obtendrá la salida procesada que genera dicho JSP.

Podemos también obtener una lista de recursos de nuestra aplicación web, con:

Set recursos = context.getResourcePaths(String ruta);

Nos devolverá el conjunto de todos los recursos que haya en la ruta indicada (relativa al contexto de la aplicación), o en cualquier subdirectorio de ella.

4.1.4. Redirecciones

Si lo que queremos es acceder a recursos dinámicos, el método anterior no nos sirve. Para ello utilizaremos estas redirecciones. Utilizaremos el objeto RequestDispatcher que nos proporciona ServletContext.

Hemos de distinguir estas redirecciones de la que se producen cuando ejecutamos

response.sendRedirect();

Con sendRedirect lo que estamos haciendo es devolver al cliente una respuesta de redirección. Es decir, será el cliente, quien tras recibir esta respuesta solicite la página a la que debe redirigirse.

Con RequestDispatcher es el servidor internamente quien solicita el recurso al que nos redirigimos, y devuelve la salida generada por éste al cliente, pero todo ello de forma transparente al cliente. En cliente no sabrá en ningún momento que se ha producido una redirección.

Para obtener un objeto RequestDispatcher podemos usar los siguientes métodos de ServletContext:

RequestDispatcher rd = context.getRequestDispatcher(ruta);
RequestDispatcher rd = context.getNamedDispatcher(ruta);

Como ruta proporcionaremos la ruta relativa al contexto de nuestra aplicación, comenzando por el carácter "/", del recurso al que nos queramos redirigir. También podemos obtener este objeto proporcionando una ruta relativa respecto al recurso actual, utilizando para ello el método getRequestDispatcher del objeto ServletRequest, en lugar de ServletContext:

RequestDispatcher rd = request.getRequestDispatcher(ruta);

Podemos utilizar el RequestDispatcher de dos formas distintas: llamando a su método include o a forward.

rd.include(request, response);

El método include incluirá el contenido generado por el recurso al que redireccionamos en la respuesta, permitiendo que se escriba este contenido en el objeto ServletResponse a continuación de lo que se haya escrito ya por parte de nuestro servlet. Se podrá llamar a este método en cualquier momento. Lo que no podrá hacer el recurso al que redireccionamos es cambiar las cabeceras de la respuesta, ya que lo único que estamos haciendo es incluir contenido en ella. Cualquier intento de cambiar cabeceras en la llamada a include será ignorado.

Si hemos realizado la redirección utilizando un método getRequestDispatcher (no mediante getNamedDispatcher), en la petición del servlet al que redireccionamos podremos acceder a los siguientes atributos:

javax.servlet.include.request_uri
javax.servlet.include.context_path
javax.servlet.include.servlet_path
javax.servlet.include.path_info
javax.servlet.include.query_string

Con los que podrá consultar la ruta desde donde fué invocado.

rd.forward(request, response);

El método forward sólo podrá ser invocado cuando todavía no se ha escrito nada en la respuesta del servlet. Esto es así porque esta llamada devolverá únicamente la salida del objeto al que nos redirigimos. Si esto no fuese así, se produciría una excepción IllegalStateException. Una vez el método forward haya devuelto el control, la salida ya habrá sido escrita completamente en la respuesta.

Si el recurso al que redireccionamos utiliza direcciones relativas, estás direcciones se considerarán relativas al servlet que ha hecho la redirección, por lo que si se encuentran en rutas distintas se producirá un error. Tenemos que hacer que las direcciones sean relativas a la raiz del servidor para que funcione correctamente (direcciones que comiencen por "/").

4.1.5. Otros métodos

La clase ServletContext nos proporciona otros métodos de utilidad, que podremos consultar accediendo a su documentación JavaDoc.

Un método de interés es log, que nos permite escribir texto en el fichero de log del servlet:

context.log(mensaje);

Esto será util para tener un registro de eventos que ocurren en nuestra web, o bien para depurar errores.

4.1.6. Listeners de contexto

Existen objetos que permanecen a la escucha de los distintos eventos que pueden ocurrir en el objeto de contexto de servlets, ServletContext.

Un primer listener, es el ServletContextListener, que nos permitirá dar respuesta a los eventos de creación y destrucción del contexto del servlet. El código para este listener será como sigue a continuación:

import javax.servlet.*;

@WebListener
public class MiContextListener implements ServletContextListener {

    public void contextDestroyed(ServletContextEvent sce) {
        // Destruccion del contexto
    }

    public void contextInitialized(ServletContextEvent sce) {
        // Inicialización del contexto
    }
}

Esto nos será de gran utilidad si necesitamos inicializar ciertas estructuras de datos que van a utilizar varios servlets. De esta forma el contexto se habrá inicializado antes de que los servlets puedan ejecutarse.

Si lo que queremos es saber cuando se ha añadido, eliminado, o modificado alguno de los atributos del contexto global, podemos utilizar un listener ServletContextAttributeListener. Los métodos que deberemos definir en este caso son los siguientes:

import javax.servlet.*;

@WebListener
public class MiContextAttributeListener
                    implements ServletContextAttributeListener {

    public void attributeAdded(ServletContextAttributeEvent scae) {
        // Se ha a&ntilde;adido un nuevo atributo
    }

    public void attributeRemoved(ServletContextAttributeEvent scae) {
        // Se ha eliminado un atributo
    }

    public void attributeReplaced(ServletContextAttributeEvent scae) {
        // Un atributo ha cambiado de valor
    }
}

Hemos visto que podemos declarar listeners mediante la anotación @WebListener. Con esto el servidor de aplicaciones reconocerá estas clases como listeners del contenedor web y los registrará de forma automática sin necesidad de hacerlo nosotros. Los listeners que podremos declarar de esta forma son:

  • ServletContextListener

  • ServletContextAttributeListener

  • ServletRequestListener

  • ServletRequestAttributeListener

  • HttpSessionListener

  • HttpSessionAttributeListener

Sin embargo, esta anotación sólo está disponible a partir de la API de Servlets 3.0. En versiones anteriores para hacer que estos objetos se registren como listeners y permanezcan a la escucha, deberemos registrarlos como tales en el descriptor de despliegue de la aplicación (web.xml.). Para ello deberemos añadir un elemento <listener> para cada objeto listener que queramos registrar:

<listener>
    <listener-class>MiContextListener</listener-class>
</listener>

<listener>
    <listener-class>MiContextAttributeListener</listener-class>
</listener>

Esta declaración no es necesaria si estamos utilizando la anotación @WebListener en Servlet 3.0.

4.1.7. Declaración dinámica de servlets

En Servlet 3.0 también tenemos la opción de configurar nuestros servlets de forma programática durante la inicialización del contexto, e incluso declarar servlets que no habían sido declarados previamente:

@WebListener
public class MiListener implements ServletContextListener {
  public void contextInitialized (ServletContextEvent sce) {
    ServletContext sc = sce.getServletContext();

    // Declara un nuevo servlet y lo configura
    ServletRegistration servletNuevo = sc.addServlet("miServlet",
      "es.ua.jtech.servlet.MiServlet");
    servletNuevo.addMapping("/UrlServlet");

    // Obtiene un servlet ya declarado para configurarlo
    ServletRegistration sr = sc.addServlet("otroServlet");
    sr.addMapping("/UrlOtroServlet");
    sr.setInitParameter("param1", "valor1");
  }
}

Por lo tanto, en Servlet 3.0 podemos declarar los servlets de tres formas distintas:

  • En el descriptor de despliegue web.xml.

  • Mediante anotaciones en la propia clase del servlet.

  • De forma programática en la inicialización del contexto.

4.2. Inyección de dependencias

Los objetos de una aplicación normalmente necesitan otros objetos para realizar su tarea. Por ejemplo, un servlet puede necesitar una fuente de datos de la que obtener la información a mostrar al cliente web. Tradicionalmente, este servlet deberá obtener el objeto del que depende, por ejemplo buscando la fuente de datos en JNDI. El patrón de inyección de dependencias (DI) consiste en invertir este comportamiento: no será el servlet quién busque la fuende de datos, sino que la fuente de datos le será inyectada al servlet de forma externa. Las ventajas de este patrón son el bajo acoplamiento existente entre el servlet y los objetos de los que depende, y la facilidad para cambiar la implementación de la dependencia sin tener que modificar el código del servlet. Por ejemplo, esto facilitará las pruebas, ya que podremos sustituir la fuente de datos original por un mock sin tener que hacer ningún cambio en el código del servlet.

En Java SE 6 se integra la inyección de dependencias mediante la especificación Contexts and Dependency Injection (CDI, JSR-299). La implementación de referencia de CDI se denomina Weld (http://seamframework.org/Weld).

4.2.1. Configuración de Weld

Weld ya se encuentra integrado en la API de Java EE 7, por lo que no es necesario añadir esta librería como dependencia de forma explícita, a no ser que estuviésemos utilizando un contenedor web que no soporte la especificación completa de Java EE.

Para poder utilizar objetos CDI deberemos añadir un fichero beans.xml al directorio WEB-INF de nuestra aplicación web. Dicho fichero puede estar vacío, pero al menos debe existir. Si no queremos dejarlo vacío, podemos utilizar la siguiente plantilla inicial:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
       http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
Podemos crear este fichero desde IntelliJ pulsando con el botón derecho sobre el directorio WEB-INF y seleccionando New > XML Configuration File > CDI beans.xml.
Creación del fichero beans.xml con IntelliJ

4.2.2. Managed beans

Podemos inyectar distintos tipos de objetos, como fuentes de datos, contextos de persistencia JPA, o casi cualquier objeto Java plano (POJOs). Nos referiremos a los objetos que inyectamos como managed beans.

Casi cualquier clase Java puede ser un managed bean, sin ser necesario anotarla de ninguna forma. Para que una clase pueda comportarse como managed bean debe cumplir lo siguiente:

  • No puede ser una clase interna, a no ser que sea de tipo static.

  • Tiene un constructor sin parámetros.

  • No tiene constructor sin parámetros, pero tiene un constructor anotado con @Inject.

Por ejemplo, el siguiente POJO podría ser inyectado como managed bean:

public class HolaMundo {
    public String saluda(String nombre) {
        return "Hola " + nombre;
    }
}

4.2.3. Inyección de objetos

Para inyectar un bean utilizamos la etiqueta @Inject. Vamos a ver el caso en el que se inyecta en un servlet, aunque podría inyectarse en cualquier otra clase (incluso dentro de otro managed bean):

@WebServlet(urlPatterns = "/miServlet")
public class MiServlet extends HttpServlet {
    @Inject
    private HolaMundo holaMundo;
}

El tiempo de vida del objeto inyectado dependerá del ámbito en el que se defina.

4.2.4. Ámbito de los beans

Los managed beans pueden existir en distintos ámbitos. Según el ámbito especificado mantendrá su estado durante un tiempo diferente:

Ámbito Descripción

@RequestScoped

Se mantiene durante el tiempo que dure la petición (request) actual. Sólo será accesible por la petición actual.

@SessionScoped

Se mantiene durante el tiempo que dure la sesión (session) actual. Será accesible por todas las peticiones que se hagan dentro de la sesión actual.

@ApplicationScoped

Se mantiene durante toda la vida de la aplicación web (contexto). Será accesible por todas las peticiones de todas las sesiones.

@Dependent

Es el valor por defecto. Se mantiene con vida mientras exista el objeto en el que se ha inyectado.

Por ejemplo, podemos etiquetar el bean anterior para que sólo se mantenga con vida durante el tiempo que dure la petición de la siguiente forma:

@RequestScoped
public class HolaMundo {
    public String saluda(String nombre) {
        return "Hola " + nombre;
    }
}
Para que un managed bean anotado con @SessionScoped funcione correctamente deberemos hacer que sea Serializable. De no ser así, obtendremos el mensaje de error Managed bean declaring a passivating scope must be passivation capable.

4.2.5. Clasificadores

Podemos crear varias versiones de un bean, y distinguirlas mediante lo que se conoce como clasificadores (qualifiers). Podemos definir clasificadores de la siguiente forma:

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Dia {}

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Tarde {}

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Noche {}

Podríamos crear distintas versiones del bean HolaMundo, y etiquetar cada una de ellas con un clasificador distinto:

@Dia
public class HolaMundoDia extends HolaMundo {
    public String saluda(String nombre) {
        return "Buenos días " + nombre;
    }
}

@Tarde
public class HolaMundoTarde extends HolaMundo {
    public String saluda(String nombre) {
        return "Buenas tardes " + nombre;
    }
}

@Noche
public class HolaMundoNoche extends HolaMundo {
    public String saluda(String nombre) {
        return "Buenas noches " + nombre;
    }
}

Si un bean no lleva clasificador, se entiende que por defecto tiene el clasificador @Default. También podemos etiquetar alguna de las versiones de forma explícita con este clasificador por defecto.

Cuando inyectamos el bean, podemos indicar qué versión queremos inyectar mediante el clasificador:

@WebServlet(urlPatterns = "/miServlet")
public class ItemServlet extends HttpServlet {
    @Inject @Tarde
    private HolaMundo holaMundo;
}

Si no indicamos clasificador, se buscará la versión @Default, de la misma forma que si hiciésemos:

@WebServlet(urlPatterns = "/miServlet")
public class ItemServlet extends HttpServlet {
    @Inject @Default
    private HolaMundo holaMundo;
}

4.2.6. Productores

Podemos definir métodos que produzcan objetos para ser inyectados. De esta forma podemos definir la forma de obtener los objetos a inyectar. Hasta ahora, al inyectar un objeto lo que se hacía era construir una nueva instancia del objeto utilizando alguno de sus constructores, e inyectar dicha instancia. Utilizando un método productor podremos construir los objetos de otras formas.

Por ejemplo, nos puede ser útil para definir una fuente de datos:

public @interface FuenteDatos {}
public class ProductorConexiones {
    @Produces @FuenteDatos
    public Connection getConnection() throws Exception {
        Context ctx = new InitialContext();
        DataSource ds = (DataSource) ctx.lookup("jdbc/miBD");
        return ds.getConnection();
    }
}

Podemos inyectar las conexiones producidas por la anterior fuente de datos:

@Inject @FuenteDatos Connection conexion;

De esta forma inyectamos objetos Connection generados por una fuente de datos, que no son instanciados utilizando un constructor, sino una factoría, y que por lo tanto no podían ser inyectados como se ha visto anteriormente.

4.2.7. Definición de alternativas

Podemos definir distintas implementaciones alternativas para los beans a inyectar, de forma que en tiempo de despliegue podamos cambiar la implementación que se va a utilizar. Esto es especialmente útil para la implementación de pruebas. Por ejemplo, si tenemos el siguiente bean:

public class FuenteDatos implements IFuenteDatos

Podemos definir una implementación alternativa que contenga un mock para realizar pruebas:

@Alternative
public class MockFuenteDatos implements IFuenteDatos

Por defecto siempre se utilizará la implementación original, a no ser que en el fichero beans.xml indiquemos el nombre de la implementación alternativa:

<beans xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
                 http://java.sun.com/xml/ns/javaee
                 http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
    <alternatives>
        <class>es.ua.jtech.MockFuenteDatos</class>
    </alternatives>
</beans>

En este caso, para usar el mock deberemos incluir el fichero beans.xml que así lo indique. También podemos crear la clase alternativa como una especialización:

@Specializes
public class MockFuenteDatos extends FuenteDatos

En este caso la especialización siempre sustituirá a la original. De esta forma, si incluimos la especialización en el carpeta de fuente de prueba (test), cuando incluyamos dichas clases en la aplicación siempre se usará el mock, mientras que en el caso de compilar únicamente las clases de la carpeta main para poner la aplicación en producción, se utilizará la implementación original. Esta es una forma sencilla de sustituir clases por mock en nuestras pruebas.

4.3. Ejercicios

4.3.1. Ejemplo de contexto (0 puntos)

Vamos a probar la aplicación cweb-contexto incluida en los ejercicios de la sesión.

a) Desplegar la aplicación web en Tomcat, y acceder a la dirección:

http://localhost:8080/cweb-contexto

Veremos la aplicación web ya en marcha.

b) La aplicación web nos permite visualizar los atributos de contexto definidos y sus valores, y añadir nuevos atributos. A parte de los atributos que nosotros añadimos manualmente, ¿hay más atributos de contexto definidos?

c) Podemos añadir nuevos atributos de contexto. Daremos un nombre del atributo, y un texto que contendrá como valor. Además como valor también se introducirá el identificador de sesión del navegador que haya creado dicho atributo. Abrir distintos navegadores y añadir atributos de contexto desde cada uno. Comprobar que en cada navegador vemos tanto los atributos creados en su sesión, como lo atributos creados creados en las sesiones de otros navegadores (el identificador de sesión será distinto).

d) Si nos fijamos en el paquete org.expertojava.cweb.contexto.listener, veremos que se ha añadido un listener sobre los atributos del contexto. Este listener imprime mensajes en el log indicando cuando se añade, elimina o reemplaza un atributo de contexto. Comprobar en el fichero de logs correspondiente que se han registrado los cambios en los atributos que hayamos hecho.

4.3.2. Chat con servlets (0.7 punto)

Vamos a realizar una aplicación de chat utilizando servlets. En el directorio cweb-chat de los fuentes de la sesión podrás encontrar la base sobre la que construiremos el chat. Cada mensaje de chat estará encapsulado en la clase Mensaje, y la lista de mensajes actual del chat se encontrará en la clase ColaMensajes.

Además se proporcionan los ficheros HTML necesarios para la aplicación. El fichero index.html contiene el formulario de login para que un usuario introduzca el nick con el que entrará en el chat (no se solicita ningún password para validar). El login se hará efectivo por el servlet LoginUsuarioServlet también proporcionado, que introducirá el nick del usuario en la información de sesión y nos redirigirá al chat. En el subdirectorio chat tendremos los ficheros estáticos del chat:

chatFrames.html

Página principal de los frames de la aplicación chat. Mostrará un frame con el formulario para enviar mensajes, y otro con la lista de mensajes enviados.

enviaMensaje.html

Formulario para enviar mensajes al chat.

error.html

Página que nos muestra un mensaje de error cuando se intenta enviar un mensaje sin haber hecho login.

cabecera.htmlf

Cabecera de la tabla de mensajes, a incluir al comienzo de la página de lista de mensajes.

pie.htmlf

Pie de la tabla de mensajes, a incluir al final de la página de lista de mensajes.

Ahora deberemos implementar los servlets para el envio de mensajes y para la consulta de la lista de mensajes enviados. Se pide:

a) La cola de mensajes será el objeto común al que acceden los servlets para el envio y la consulta de estos mensajes. Por lo tanto el objeto deberá añadirse como atributo del contexto. Esto lo tendremos que hacer antes de que cualquier servlet se haya ejecutado. Para ello debemos crear un objeto ServletContextListener que en la creación del contexto inicialice la cola de mensajes (ColaMensajes) y la introduzca como atributo en el contexto global (atributo org.expertojava.cweb.chat.mensajes).

b) Una vez tenemos creada la cola de mensajes, deberemos implementar el servlet EnviaMensajeServlet, que tome un mensaje como parámetro (el nombre del parámetro es texto), y lo añada a la lista de mensajes con el nick del usuario actual (obtenido del atributo org.expertojava.cweb.chat.nick de la sesión). Una vez enviado el mensaje, mostraremos en la salida el contenido de enviaMensaje.html, mediante un objeto RequestDispatcher. Si no hubiese ningún usuario en la sesión, no se registrará el mensaje y se deberá redirigir la salida a error.html

c) Por último, deberemos implementar el servlet ListaMensajesServlet que mostrará todos los mensajes del chat. Este servlet debe:

  • Para que la lista de mensajes se actualice periodicamente en el cliente, haremos que se recargue cada 5 segundos. Añadir la cabecera HTTP correspondiente a la respuesta para que esto ocurra (cabecera Refresh, indicando como valor el número de segundos que tardará en recargar).

  • Incluir el contenido del fichero estático cabecera.htmlf al comienzo del documento generado, y pie.htmlf al final, para enmarcar la zona donde aparecen los mensajes del chat.

    Se debe obtener el PrintWriter para escribir la respuesta antes de hacer el primer include, ya que de lo contrario se obtendría una excepción de tipo IllegalStateException. Esto de debido a que el include ya habría obtenido previamente el flujo de salida de la respuesta.
  • Obtener el nick del usuario actual de la sesión. Los mensajes enviados con este nick se mostrarán en negrita, el resto se mostrarán de forma normal.

d) Comprobar que el chat funciona correctamente. Conectar desde varios clientes a un mismo servidor.

4.3.3. Inyección de dependencias (0.3 puntos)

Utiliza inyección de dependencias para gestionar la cola de mensajes del chat anterior. En este caso:

  • Deberás configurar correctamente los ficheros web.xml y beans.xml.

  • Deberás crear un bean que gestione la cola de mensajes del chat.

  • Deberás especificar el ámbito adecuado en el bean anterior.

  • Ya no será necesario utilizar un listener del contexto para inicializarla, se inicializará la primera vez que se inyecte.

  • Ya no será necesario acceder al atributo de contexto con la cola de mensajes. La cola será inyectada en los servlets.