The Servlet Technology Model (SCWCD)

Objetivos:
Section 1: The Servlet Technology Model
1.1- For each of the HTTP Methods (such as GET, POST, HEAD, and so on) describe the purpose of the method and the technical characteristics of the HTTP Method protocol, list triggers that might cause a Client (usually a Web browser) to use the method; and identify the HttpServlet method that corresponds to the HTTP Method.
1.2- Using the HttpServletRequest interface, write code to retrieve HTML form parameters from the request, retrieve HTTP request header information, or retrieve cookies from the request.
1.3- Using the HttpServletResponse interface, write code to set an HTTP response header, set the content type of the response, acquire a text stream for the response, acquire a binary stream for the response, redirect an HTTP request to another URL, or add cookies to the response.
1.4- Describe the purpose and event sequence of the servlet life cycle: (1) servlet class loading, (2) servlet instantiation, (3) call the init method, (4) call the service method, and (5) call destroy method.


Métodos HTTP
  • GET: Se utiliza para obtener recursos activos o pasivos, generalmente  para archivos HTML o imágenes.
  • POST: Se utiliza generalmente para enviar grandes cantidades de información, subir archivos binarios, o para enviar nombres de usuarios y contraseñas para que no sean vistas como partes de una URL. La información se envía en el cuerpo de la petición.
  • HEAD: Es similar al método GET, salvo que solo devuelve los encabezados de respuesta y no devuelve ningún mensaje en el cuerpo. Se utiliza generalmente para determinar el estado de un recurso y verificar la fecha de última modificación.
  • TRACE: Se utiliza para ver cuáles son los datos que se están enviando al servidor. De esta manera el cliente puede ver que es lo que envía al otro lado, es útil para realizar pruebas y para la resolución de casos.
  • PUT: Se utiliza para guardar la información adjuntada en el cuerpo de la petición (similar a POST).
  • DELETE: Se utiliza para indicar que se debe eliminar el recurso especificado con la URL.
  • OPTIONS: Permite obtener una lista de los métodos HTTP, los cuales se pueden utilizar para responder a cierto recurso.
  • CONNECT: Se utiliza para propósitos de tunneling (enrutamientos).
Manejando peticiones HTTP en un HttpServlet
Para cada método HTTP, existe un correspondiente método en la clase HttpServlet que tiene la signatura (donde doXXX depende del método HTTP):
protected void doXXX (HttpServletRequest, HttpServletResponse) throws ServletException, IOException;
  • GET: doGet()
  • POST: doPost()
  • HEAD: doHead()
  • TRACE: doTrace()
  • PUT: doPut()
  • DELETE: doDelete()
  • OPTIONS: doOptions()
Flujo de control del contenedor de servlets:
  1. El contenedor de servlets invoca el método de HttpServlet: service (ServletRequest, ServletResponse).
  2. El método anterior invoca al método (sobrecargado en la misma clase): service (HttpServletRequest, HttpServletResponse).
  3. El método anterior analiza la petición y dependiendo del método HTTP utilizado invoca el correspondiente método doXXX() del servlet.
Es aconsejable no sobrescribir el método service(), debido a que se pierde la funcionalidad  de la clase HttpServlet. Se recomienda solo sobrescribir los métodos doGet() y doPost().
Muchos de los componentes más importantes de la API de Servlets, incluyen las interfaces HttpServletRequest y HttpServletResponse. Es el contenedor de servlets el que provee las clases que implementan dichas interfaces.
En caso de que se intente realizar una petición a un servlet, el cual no haya sobrescrito el método correspondiente (doGet() o doPost()) se devolverá un error 405 indicando dicha problemática. Es decir, si un servlet solo posee el método doGet(), al mismo solo se podrá acceder a través de este método HTTP y no otro (si se intenta se obtendrá el error 405).

Analizando las peticiones (javax.servlet.ServletRequest y javax.servlet.HttpServletRequest)
Las interfaces ServletRequest y HttpServletRequest permiten analizar una petición. Proveen una vista de la información enviada desde el navegador. La interface ServletRequest provee métodos relevantes para cualquier protocolo, mientras que HttpServletRequest agrega otros específicos para el protocolo HTTP.


Métodos pertenecientes a la interface ServletRequest para obtener los parámetros enviados por un cliente:
  • String getParameter (String paramName): Devuelve el valor asociado al parámetro.
  • String[] getParameterValues (String paramName): Devuelve un arreglo de todos los valores asociados a un parámetro.
  • Enumeration getParameterNames (): Devuelve un objeto Enumeration con todos los nombres de los parámetros.
Métodos de HttpServletRequest para obtener los encabezados de un request (los headers son específicos del protocolo http):
  • String getHeader (String headerName): Devuelve el valor asociado al encabezado.
  • Enumeration getHeaders (String headerName): Devuelve un objeto Enumeration con todos los valores asociados a un encabezado.
  • Enumeration getHeadersNames (): Devuelve un objeto Enumeration con todos los nombres de los encabezados.
Enviando las respuestas (javax.servlet.ServletResponse y javax.servlet.http.HttpServletResponse)
La interface ServletResponse provee métodos relevantes para cualquier protocolo, mientras que HttpServletResponse agrega otros específicos para el protocolo HTTP. Una instancia de HttpServletResponse funciona como la vía para que el servlet envíe información de vuelta al navegador.

Métodos de la interface ServletResponse:
  • PrintWriter getWriter (): Devuelve un objeto java.io.PrintWriter el cuál puede ser utilizado para enviar caracteres al cliente. Este objeto se utiliza extensivamente en los servlets para generar páginas dinámicas HTML.
  • ServletOutputStream getOutputStream (): Devuelve un objeto javax.servlet.ServletOutputStream el cual permite enviar archivos binarios al cliente. Dicho objeto hereda de java.io.OutputStream.
  • void setContentType (String type): Permite especificar el tipo MIME de la información que se devuelve en la respuesta de una petición. También se puede incluir la codificación de caracteres utilizada en la respuesta. Este método se debe invocar antes del método getWriter(), para así obtener un objeto PrintWriter con esta información seteada.
Solo uno de los métodos getWriter() y getOutputStream() puede ser invocado en una instancia de un objeto ServletResponse. En caso contrario, la invocación al segundo método lanzará la excepción IllegalStateException.

Existen cuatro capacidades importantes de la interface HttpServletResponse:
- Setear encabezados de respuesta: Posee siete métodos para llevar a cabo esta funcionalidad:
  • void setHeader (String name, String value)
  • void setIntHeader (String name, int value)
  • void setDateHeader (String name, long milisec)
  • void addHeader (String name, String value)
  • void addIntHeader (String name, int value)
  • void addDateHeader (String name, long milisec)
  • boolean containsHeader (String name)
- Redireccionamiento de peticiones: El método HttpServletResponse.sendRedirect (String location). Este método no puede ser invocado si la respuesta ya ha sido enviada al cliente, si se invoca se lanzará la excepción IllegalStateException. El método sendRedirect() no es transparente al navegador, el servlet envía un mensaje al navegador para que este obtenga el recurso en otra parte.
- Setear cookies: El método HttpServletResponse.addCookie (Cookie cookie), permite agregar cookies a la respuesta del servlet.
- Enviar códigos de estado en la respuesta: El protocolo HTTP define códigos de estado para las condiciones de error comunes. Todos los códigos están definidos como constantes en la interface HttpServletResponse. Esta interface también provee los métodos sendError (int status_code) y sendError (int status_code, String msg) los cuales envían un código de estado al cliente.

Ciclo de vida de un Servlet
Antes de que un servlet pueda dar servicio a los clientes, el contenedor de servlets debe realizar ciertos pasos:

1.    Carga e instanciación de un servlet: Cuando se inicia el contenedor de servlets, este revisa todos los archivos de configuración de todas las aplicaciones web que aloja. Cada aplicación web posee un web.xml el cual tiene una entrada por cada servlet que utiliza. El contenedor de servlets carga cada servlet a través de la instrucción: Class.forName(className).newInstance(). Es por esto que cada servlet debe tener un constructor público sin argumentos. En caso contrario, se lanza la excepción java.lang.InstantiationException envuelta dentro de una javax.servlet.ServletException.
2.    Inicialización de un servlet: Como los servlets se instancian a partir de un constructor sin argumentos (generalmente el por defecto), se utiliza el método init(ServletConfig) para realizar la inicialización de los datos del servlet. El contenedor de servlets luego de instanciar el servlet, invoca este método pasando como argumento un objeto ServletConfig. Dicho objeto contiene todos los parámetros de inicialización especificados en el xml de configuración (web.xml).

La clase javax.servlet.GenericServlet posee dos métodos init(): uno con un parámetro de tipo ServletConfig y otro sin parámetros. El método init(ServletConfig) se encarga de guardar el objeto ServletConfig en el servlet e invoca el método init(). El método init() se puede sobrescribir libremente para realizar una inicialización específica. Si se sobrescribe el método init(ServletConfig) se debe incluir la invocación al método super.init(ServletConfig) para no perder  la referencia al objeto ServletConfig (la cual se obtiene a través del método getServletConfig()).

Generalmente el contenedor de servlets inicializa un servlet cuando recibe la primera petición. Si la inicialización realiza muchas tareas, el tiempo de respuesta del primer cliente va a ser muy malo. En los casos en donde esto es inaceptable, el archivo de configuración web.xml tiene la etiqueta , la cual especifica al contenedor de servlets que debe cargar e inicializar uno o más servlets automáticamente cuando el mismo se inicia. Estos dos tipos de carga e inicialización se denominan: carga perezosa (lazy loading) y precarga o preinicialización.
3.    Dando servicio a las peticiones: Cuando el contenedor de servlets recibe una petición, despacha la misma a una instancia de un servlet invocando el método Servlet.service(ServletRequest, ServletResponse).
4.    Destruyendo un servlet: Cuando el contenedor de servlets decide que un servlet ya no es necesario, él invoca el método destroy() sobre la instancia del servlet. Dicho método libera los recursos, y una vez que es invocado el servlet se encuentra fuera de servicio (el contenedor no puede volver a ejecutar el método service()). Antes de invocar este método, el contenedor de servlets espera a que se finalice la ejecución, por parte de todos los hilos en ejecución, del método service().

Causas por las que el contenedor de servlets puede invocar el método destroy() sobre un servlet:

  • Si el servlet está funcionando lento sobre los recursos o ninguna petición ha llegado al servlet por un largo tiempo.
  • Si el contenedor de servlets mantiene un pool de instancias de servlets, puede crear y destruir las instancias.
  • Si el contenedor de servlets detiene sus servicios, es decir, si es apagado.
5.    Descargando un servlet: Una vez destruida, la instancia del servlet puede ser eliminada por el garbage collector, en este caso se dice que el servlet ha sido descargado.

javax.servlet.ServletConfig
Esta interface provee métodos solo para obtener los parámetros del archivo de configuración web.xml:
  • String getInitParameter (String name): Devuelve el valor del parámetro.
  • Enumeration getInitParameterNames (): Devuelve un objeto Enumeration con todos los nombres de los parámetros.
  • ServletContext getServletContext (): Devuelve la instancia de ServletContext del servlet actual.
  • String getServletName (): Devuelve el nombre del servlet especificado en el archivo de configuración.
javax.servlet.ServletContext
Esta interface permite a los servlets obtener información sobre el ambiente en donde se están ejecutando. Toda aplicación web tiene una sola instancia de ServletContext, y la misma es accesible por todos los recursos activos de la aplicación. También se utiliza para compartir información entre servlets.
Métodos de la interface:
  • java.net.URL getResource (String path): Devuelve una instancia de URL del recurso que apunta el path. El path debe comenzar con un carácter ‘/’ el cuál especifica el directorio raíz de la aplicación web.
  • java.io.InputStream getResourceAsStream (String path): Es un atajo para obtener el InputStream de salida de un recurso. Es equivalente a getResource(path).openStream().
  • java.lang.String getRealPath (String relativePath): Devuelve el path absoluto del recurso, es decir, su path en el sistema de archivos.
Compartiendo datos entre servlets
Los servlets pueden compartir información a través de tres objetos contenedores. Los mismos difieren en la visibilidad de la información contenida.
  • javax.servlet.ServletRequest: Puede compartir información con cualquier servlet que se ejecute en la misma petición.
  • javax.servlet.http.HttpSession: Puede compartir información con cualquier servlet solo mientras sea válida la sesión del cliente.
  • javax.servlet.ServletContext: Puede compartir información con cualquier servlet de la misma aplicación web.
Estos tres contenedores de información poseen los siguientes métodos para setear y obtener datos:
  • Object getAttribute (String name): Devuelve el objeto almacenado o null.
  • Enumeration getAttributeNames (): Devuelve un objeto Enumeration que contiene los nombres de todos los atributos.
  • void setAttribute (String name, Object value): Agrega una pareja clave-valor al contenedor.
  • void removeAttribute (String name): Elimina el atributo en el contenedor.
javax.servlet.RequestDispatcher
Las interfaces javax.servlet.ServletContext y javax.servlet.ServletRequest poseen un método para obtener un objeto de tipo RequestDispatcher, el método es:
        public RequestDispatcher getRequestDispatcher (String path)

La interface RequestDispatcher posee dos métodos:
  • void forward (ServletRequest request, ServletResponse response): Permite a un servlet procesar una petición parcialmente y luego pasar la petición a otro servlet para generar la respuesta final. Este método puede ser invocado solo si la respuesta no fue enviada, caso contrario se lanza la excepción IllegalStateException. Es completamente manejado en el servidor, a diferencia de HttpServletResponse.sendRedirect(String location).
  • void include (ServletRequest request, ServletResponse response): Permite incluir el contenido de otro recurso en la respuesta que está generando el recurso que invoca el método.
La interface ServletContext además provee un método que permite redireccionar peticiones a componentes especificando solo su nombre (el cuál se define en el archivo web.xml), en vez de utilizar una URI. Este método es:
public RequestDispatcher getNamedDispatcher (String name)
Existe una diferencia importante entre el método getRequestDispatcher() de ServletContext y ServletRequest. Al método getRequestDispatcher() de ServletRequest se le puede pasar un path relativo, mientras que al de ServletContext no. El path pasado al método de ServletContext debe comenzar con ‘/’.

Accediendo a los atributos de la petición con RequestDispatcher
El servlet incluido o redireccionado puede acceder a la información de la petición original a través de diversos atributos. Los nombres de los atributos dependen de si se ha invocado el método forward() o include(), los mismos pueden ser:
- include():
  • 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
- forward():
  • javax.servlet.forward.request_uri
  • javax.servlet.forward.context_path
  • javax.servlet.forward.servlet_path
  • javax.servlet.forward.path_info
  • javax.servlet.forward.query_string
Los valores de estos atributos son los mismos que los métodos: getRequestURI(), getContextPath(), getServletPath(), getPathInfo() y getQueryString de la interface HttpServletRequest. Estos atributos se acceden como los atributos regulares, a través del método getAttribute().


RESUMEN
Ciclo de vida de un Servlet y API
  • El contenedor inicializa un servlet cargando su clase, invocando su constructor sin argumentos, e invocando el método init().
  • El método init() (el cual puede sobrescribir el programador) solo es invocado una vez en la vida del servlet, y siempre antes de que este pueda dar servicio a cualquier petición cliente.
  • El método init() brinda (al servlet) acceso a los objetos ServletConfig y ServletContext, los cuales el servlet necesita para obtener información sobre su propia configuración y la de la aplicación web.
  • El contenedor finaliza la vida de un servlet invocando su método destroy().
  • La mayoría del tiempo de vida de un servlet se pasa ejecutando el método service() para una petición de un cliente.
  • Cada petición a un servlet se ejecuta en un thread separado. Solo existe una instancia de una clase servlet en particular.
  • Los servlets deben extender (en su mayoría) de la clase javax.servlet.http.HttpServlet, es en esta clase abstracta en donde se sobrecarga el método service() para que tome como parámetros los objetos HttpServletRequest y HttpServletResponse.
  • HttpServlet extiende de la clase abstracta javax.servlet.GenericServlet, la cual implementa la mayoría de los métodos básicos del servlet.
  • La clase GenericServlet implementa las interfaces Servlet y ServletConfig.
  • Las clases servlets se encuentran en uno de los dos paquetes: javax.servlet y javax.sevlet.http.
  • Se puede sobrescribir el método init(), y se debe sobrescribir al menos un método de servicio (doGet(), doPost(), etc.).
HTTP y HttpServletRequest
  • Los métodos doGet() y doPost() de HttpServlet toman como argumentos los objetos HttpServletRequest y HttpServletResponse.
  • El método service() determina que método de servicio (doGet(), doPost(), etc.) se debe ejecutar basado en el método HTTP (GET, POST, etc.).
  • Las peticiones POST poseen un cuerpo, las peticiones GET no, aunque estas últimas pueden contener parámetros de petición en su URL.
  • Las peticiones GET son idempotentes (idempotent). Es decir, son capaces de ejecutarse múltiples veces sin causar ningún efecto en el servidor. 
  • Las peticiones POST no son idempotentes. Por este motivo, se debe diseñar el código de manera que si el cliente manda una misma petición dos veces  (por error), se pueda manejar de una manera correcta.
HttpServletResponse
  • Los objetos Response se utilizan para enviar información de respuesta al cliente.
  • Los métodos que más se utilizan en el objeto de respuesta HttpServletResponse son setContentType() y getWriter().
  • El método getWriter() permite realizar operaciones de escritura de HTML (u otro tipo de texto) en el flujo de salida.
  • En la práctica, se utilizan más las paginas JSP para enviar HTML de respuesta, pero los servlets se pueden utilizar para enviar información binaria (como el caso de archivos) al cliente.
  • El método para obtener un flujo de salida binario es getOutputStream().
  • El método setContentType() permite indicar al browser como debe manejar la información que se envía en la respuesta. Los valores típicos son: “text/html”, “application/pdf”, y “image/jpeg”.
  • Se pueden setear los encabezados de respuesta utilizando los métodos addHeader() y setHeader(). La diferencia entre ambos depende es lo que se encuentra seteado en los encabezados. Es decir, setHeader() reemplaza el valor del encabezado, mientras que addHeader() agrega un valor adicional al encabezado ya existente. En caso de que el encabezado no exista ambos métodos se comportan del mismo modo.
  • El método sendRedirec() redirige una petición hacia otro recurso. Enviando el código 301 al browser, el cual es el que realiza la redirección al recurso especificado en la respuesta HTTP. 
  • No se puede invocar el método sendRedirect() una vez que la respuesta ya fue invocada al cliente. En caso de que se invoque el método en esta situación, se lanzará la excepción IllegalStateException.

0 comentarios :: The Servlet Technology Model (SCWCD)

Publicar un comentario en la entrada