Section 3: The Web Container Model
 

 

3.1 For the ServletContext initialization parameters: write servlet code to access initialization parameters; and create the deployment descriptor elements for declaring initialization parameters.

ServletContext es una interfase que el web container se encargará de inicializar con información relacionada con la aplicación web. Este objeto será usado por los servlets para obtener información de la aplicación, de otros servlets o para transferir información a otros servlets.

El tag <context-param> que puede encontrarse 0,1 o mas veces debajo de <web-app> sirve para especificar un parametro de aplicación. El formato es el siguiente (igual al de los parametros de servlet):

<context-param>
   <param-name>Nombre</param-name>
   <param-value>Jose</param-value>
</context-param>

Desde cualquier servlet, se puede obtener una referencia al ServletContext mediante el método: getServletContext(). Este método esta implementado en la clase GenericServlet (ya que GenericServlet implementa la interfase ServletConfig), de la cual se deriva nuestro servlet. Los métodos para obtener los parametros son exactamente iguales a los métodos para obtener los parametros de inicialización de un servlet.

- getInitParameter()

- getInitParameters()

3.2 For the fundamental servlet attribute scopes (request, session, and context): write servlet code to add, retrieve, and remove attributes; given a usage scenario, identify the proper scope for an attribute; and identify multi-threading issues associated with each scope.

Los atributos representan información que se puede agregar o sacar de cualquiera de los 3 scopes. Un atributo siempre esta representado por un map:

String --> Objeto

Los métodos son los siguientes, cualquiera sea el scope:

public void setAttribute (String name,Object value) - para setear un atributo.

public Object getAttribute (String name) - para obtener el atributo.

** Es importante recordar que cuando obtenemos un atributo de esta manera, es necesario castearlo. Por ejemplo, si quisieramos obtener un String, esta sería la sintaxis:

(String)req.getSession.getAttribute("atributoEnCuestion")

public Enumeration getAttribute Names() - para obtener una Enumeration de todos los atributos.

public void remove Attribute (String name) - para remover un atributo.

Scopes

request: Este scope empieza cuando se hace un request y termina cuando es enviada la respuesta. En el medio pueden haber llamadas a POJOs o forwardear el requerimiento a otro servlet. Muchas veces, cuando se forwardea el requerimiento se agregan atributos al objeto HttpServletRequest, que serán usados por el servlet receptor.

session: El web container crea una sesión (un objeto de tipo HttpSession) cuando un browser manda el primer requerimiento y lo conserva hasta que esa ventana del browser es cerrada (En realidad una sesión pasa por 2 estados: el primer requerimiento crea una sesión y la pone en estado 'nueva'. Cuando el cliente demuestra que es capaz de unirse a la sesión, esta queda 'establecida'. Es común agregar atributos a la sesión para poder establecer comunicaciones más largas entre el usuario y la aplicación (el carrito de compras es el ejemplo más común).

context: Este scope dura lo que dura la aplicación.

Multithreading

- Los atributos del RequestServlet son thread-safe

- Los atributos de un HttpSession no son thread-safe

- Los atributos del ServletContext no son thread-safe

- Las variables locales son thread-safe

- Las variables de instancia no son thread-safe

- Las variables estaticas no son thread-safe

3.3 Describe the Web container request processing model; write and configure a filter; create a request or response wrapper; and given a design problem, describe how to apply a filter or a wrapper.

Los filtros se pueden ejecutar antes y/o después de los servlets para agregar procesamiento que seguramente no tiene que ver con el proposito principal del servlet (osea, no agrega a la lógica de negocios del servlet, por ejemplo autorización, logging, etc.).

Para crear un filtro, creamos una clase que implementa la interfase Filter, que tiene 3 métodos:

init(FilterConfig config): Este método se ejecuta cuando se instancia el filtro. El FilterConfig puede ser guardado como una variable de instancia del filtro, para poder acceder al nombre del filtro - getFilterName() -, a los paramétros de inicialización - getInitParameter(String nombre) - o al ServletContext - getServletContext() -.

doFilter(ServletRequest request, ServletResponse response, FilterChain chain): Aca se realiza todo el procesamiento necesario y se invoca (o no) al método doFilter() del objeto chain para seguir con el procesamiento en el recurso requerido.

destroy(): Este método se invoca cuando el filtro va a ser dealocado y sirve para liberar recursos.

Tags en el deployment descriptor

<filter>
   <filter-name></filter-name>
   <filter-class></filter-class>
 </filter>


<filter-mapping>
   <filter-name></filter-name>
   <url-pattern></url-pattern> o <servlet-name></servlet-name>

</filter-mapping>

La idea es exactamente la misma que para los servlets. También se puede especificar directamente el nombre del servlet. un 3er tag que se puede agregar en el mapping es <dispatcher>, con valores REQUEST, FORWARD, INCLUDE, ERROR. A través de este tag, se puede especificar si queremos ejecutar el filtro cuando se hace un request de un servlet (valor por default), cuando se hace un forward o include interno o cuando el servlet va a ser ejecutado por una condición de error.

** Se puede agregar más de un filtro para un servlet. En ese caso, el orden en que se agregan en el deployment descriptor define que filtro se ejecutará primero.

Wrappers

Este mecanismo emula el design patter 'decorator', que envuelve a un objeto con nueva funcionalidad (o modifica la existenete sobreescribiendo algun método). El objeto wrapper se instancia en el filtro y se pasa al servlet. Para implementar estas clases wrappers, 4 clases son provistas

  • javax.servlet.ServletRequestWrapper

  • javax.servlet.ServletResponseWrapper

  • javax.servlet.http.HttpServletRequestWrapper

  • javax.servlet.http.HttpServletResponseWrapper

    Nosotros debemos extender alguna de estas clases para crear nuestros filtros.

    3.4 Describe the Web container life cycle event model for requests, sessions, and web applications;create and configure listener classes for each scope life cycle; create and configure scope attribute listener classes; and given a scenario, identify the proper attribute listener to use.

  • Los listeners son objetos que son llamados por el web container cuando ciertos eventos suceden. Para crear un listener, básicamente hay que:

    1. Programar una clase que implement el listener

    2. Poner una entrada con este listener en el deployment descriptor.

    A nivel de request, tenemos 2 listeners

    ServletRequestListener - Esta interface posee 2 métodos.

    - requestInitialized(ServletRequestEvent) es llamado cuando se recibe el requerimiento.  Recibe un parametro de tipo ServletRequestEvent. Este evento nos sirve para obtener el request -getServletRequest() - o el contexto - getServletContext().

    - requestDestroyed(ServletRequestEvent) es llamado cuando termina el método service().

    ServletRequestAttributeListener - Esta interface posee 3 métodos

    - attributeAdded(ServletRequestAttributeEvent) - es llamado cuando se agrega un atributo al requerimiento. A través de los métodos getName() y getValue() se pueden obtener los valores del atributo.

    - attributeReplaced(ServletRequestAttributeEvent) - es llamado cuando se reemplaza un atributo. getValue() retorna el nuevo objeto!

    - attributeRemoved(ServletRequestAttributeEvent) - es llamado cuando se borra un atributo. getValue() retorna el objeto removido.

    ** ServletRequestAttributeEvent extiende a ServletRequestEvent, por lo que estos eventos se pueden utilizar para obtener el request y el context.

    A nivel de context, los patterns son los mismos:

    ServletContextListener

    ContextInitialized(ServletContextEvent)

    ContextDestroyed(ServletContextEvent)

    ** Cuando la aplicación es distribuida (osea que existe más de una JVM) se crea un listener para cada una de las JVMs. En consecuencia, los 2 eventos son llamados en los 2 listeners.

  • ServletContextAttributeListener - Los métodos son los mismos, pero son llamados cuando atributos son insertados, reemplazados o borrados del contexto.

    Dentro del deployment descriptor, todos los listeners se definen de esta manera:

    <listener>

       <listener-class></listener-class>

    <listener>

    ** Que pasa si existe más de un listener (para el mismo environment) ? Los listeners son registrados en el web container en el orden que se encuentran en el deployment descriptor (osea, sus métodos de inicialización serán llamados en ese orden). Para el caso de la destrucción, los llamados van a ser inversos (osea se va a empezar por el último hasta terminar por el primero).

    ** Ahora que ya se han repasado Servlets, Filters y listeners sería bueno definir cual es el orden de instanciación de estos elementos:

    1. Los listeners son instanciados.

    2. Si alguno de los listeners implementa ContextListener, el método contextInitialized() es llamado.

    3. Los filtros son instanciados.

    4. Los servlets que en el deployment descriptor tengan <load-on-startup> son instanciados. 

    3.5 Describe the RequestDispatcher mechanism; write servlet code to create a request dispatcher; write servlet code to forward or include the target resource; and identify and describe the additional request-scoped attributes provided by the container to the target resource.

    El mecánismo de RequestDispatcher nos permite forwardear un request a otro servlet/JSP o incluso HTML (Este mecanismo es el usado en los frameworks MVC para reenviar el requerimiento a un servlet y luego para reenviarlo a la capa de presentación). El mecanismo consiste en obtener un objeto RequestDispatcher que apunte a la nueva URI. Tenemos 3 formas de obtener este objeto:

    1. A través del ServletRequest con el método getRequestDispatcher(String path): el path puede ser absoluto, desde el context root o relativo, desde la URL original (el 2do se verá afectado si se afectan los mappings).

    2. A través del ServletContext con el método getRequestDispatcher(String path): La diferencia con el anterior es que solamente pueden incluirse paths absolutos.

    3. A través del ServletContext con el método getNamedDispatcher(String nombre): El nombre se refiere al nombre de un servlet o de un JSP (Este método puede ser usado cuando no existe una entrada en <servlet-mapping> para el servlet en cuestión.

    ** los 3 métodos retornan NULL en caso de no matchear ninguna URL en la app.

    Una vez que obtuvimos el RequestDispatcher, podemos utilizar 2 métodos, forward() e include().

    forward() - La sintaxis es la siguiente:

    void forward(ServletRequest request, ServletResponse response)
              Forwards a request from a servlet to another resource (servlet, JSP file, or HTML file) on the server.

    La especificación dice que este método puede llamarse solamente si no se ha enviado nada a la salida. Sin embargo, si se ha enviado pero todavía esta en el response buffer, esta es deshechada y la invocación se puede realizar. Si ya se hizo un commit, una IllegalStateException es arrojada. Toda la respuesta es enviada desde el nuevo servlet. Cuando usamos cualquiera de los 2 métodos (forward o include), podemos obtener toda la información del servlet original a través de los siguientes atributos del ServletRequest.

    javax.servlet.forward.request_uri - corresponde a getRequestURI() en el servlet original

    javax.servlet.forward.context_path - corresponde a getContextPath en el servlet original

    javax.servlet.forward.servlet_path - corresponde a getServletPath en el servlet original

    javax.servlet.forward.path_info - corresponder a getPathInfo en el servlet original

    javax.servlet.forward.query_string - corresponde a getQueryString en el servlet original

    Include() - La sintaxis es la siguiente:

    void include(ServletRequest request, ServletResponse response)
              Includes the content of a resource (servlet, JSP page, HTML file) in the response.

    Este método incluye toda la salida del servlet incluida en la respuesta. Este método también tiene los 5 métodos anteriores, pero esta vez es el significado es completamente inverso. Los atributos, cuando son obtenidos desde el servlet incluido devuelven los valores correspondientes a ese servlet. Por el contrario, las funciones devuelven los valores correspondientes al servlet desde el cual se incluyo este servlet.