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.

HTTP es el protocolo usado por la WWW para transimir información (generalmente mediante TCP). Mas formalmente, HTTP es un protocolo de red cliente-servidor. El cliente (generalmente un browser) abre una conexión y genera un requerimiento al servidor HTTP. El servidor procesa el requerimiento, envia el resultado al cliente y cierra la conexión (es por eso que HTTP es un protocolo stateless).

Un request y un response HTTP

Un requerimiento consta de 3 partes: una linea de requerimiento, un conjunto de headers y un cuerpo de mensaje opcional. La linea de requerimiento a su vez consta de 3 partes: El método HTTP, un puntero al recurso seleccionado y el protocolo empleado. 

Requerimiento GET

El requerimiento GET es el más común de los requerimientos y sirve para pedirle un recurso al servidor HTTP. El formato de GET es el siguiente:

  • GET <URL> <PROTOCOL> (ejemplo: GET /path/to/file/index.html HTTP/1.0)
  • <HEADERS>*
  • Una linea en blanco
  • El mensaje

* Una manera de ver (Existe gente que necesita ver para creer!!) el requerimiento que estamos enviando desde el browser al servidor web es instalando un Personal Proxy. Yo instalé uno llamado WebScarab. La instalación es sumamente sencilla: basicamente, tenemos que apuntar nuestro browser al listener del personal proxy (que por default es 127.0.0.1:8008). Interceptando el requerimiento a http://www.google.com puedo ver lo siguiente:

GET http://www.google.com.ar:80/ HTTP/1.0
Accept: */*
Accept-Language: en-us
Pragma: no-cache
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)
Host:
www.google.com.ar
Proxy-Connection: Keep-Alive
Cookie: PREF=ID=ca22a5a801390fd9:TM=1130776245:LM=1130776245:S=fW_

Aqui, excepto la primera linea, todos las demás son headers.

El formato de la respuesta solo varía en la primera.

<HTTP version> <status code> <comentarios>

Esta es la respuesta que se recibe del primer requerimiento (después recibi otras, que no voy a pegarlas aquí):

HTTP/1.0 200 OK
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Server: GWS/2.1
Date: Fri, 25 Aug 2006 21:00:00 GMT
Connection: Close

<html><head>...etc!!!

**Nota: Otra forma de ver los resultados del servidor web es abrir una sesión de telnet contra el servidor web.

Cuales son los triggers en el web browser para un requerimiento de este tipo?

1. El navegante tipea una dirección en la barra de direcciones del explorador + Enter.

2. El navegante hace click en un link

3. El navegante hace el submit de un form, cuyo método es GET. 

POST

Un requerimiento POST sirve para enviar información al servidor, para que éste la procese de alguna manera y retorne un resultado (es decir que el requerimiento POST busca modificar - Insert/Delete/update - algo en el servidor). La gran diferencia con el requerimiento GET es que en el cuerpo del mensaje se envia información. El uso mas común de POST es para enviar un formulario, y es por eso que elegimos esto para el ejemplo. La URL de este sencillo ejemplo es:

http://www.w3schools.com/html/tryit.asp?filename=tryhtml_input

Y esto es lo que intercepto webscarab:

POST http://www.w3schools.com:80/html/tryit_view.asp HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer:
http://www.w3schools.com/html/tryit.asp?filename=tryhtml_input
Accept-Language: en-us
Content-Type: application/x-www-form-urlencoded
Proxy-Connection: Keep-Alive
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)
Host:
www.w3schools.com
Content-length: 330
Pragma: no-cache
Cookie: ASPSESSIONIDCSQRBTRS=GKJELMNDNILBEKEIOKGCPMCO

submit=Edit+the+text+and+click+me&code=%3Chtml%3E%0D%0A%3Cbody%3E%0D%0A%0D%0A%3Cform%3E%0D%0AFirst+name%3A+%0D%0A%3Cinput+type%3D%22text%22+name%3D%22firstname%22%3E%0D%0A%3Cbr%3E%0D%0ALast+name%3A+%0D%0A%3Cinput+type%3D%22text%22+name%3D%22lastname%22%3E%0D%0A%3C%2Fform%3E%0D%0A%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E%0D%0A%0D%0A

 ** Cual es la diferencia más importante entre GET y PUT? El requerimiento GET envia los parametros apendeados en la URL. Este tiene 2 consecuencias: la primera es que la cantidad de caracteres que se envian es limitada y la segunda es que los parametros son más visibles.

HEAD

El requerimiento HEAD es igual al GET, pero solo devuelve los headers. Esto sirve para chequear la información del URI sin tener que traer todo el contenido. Un uso de esto puede ser en un browser o en una aplicación que manejan un cache. El header retorna la fecha en la que el recurso fue modificado y de acuerdo a eso trae o no el recurso.

OPTIONS

El requerimiento OPTIONS sirve para identificar cuales son los requerimientos que la URL acepta.

PUT

El requerimiento PUT sirve para subir un archivo a la URL indicada. Es exactamente lo mismo que hacer un FTP. Si el recurso en el servidor fue reemplazado, el servidor retorna un código de estado 200 mientras que si el recurso fue creado, el servidor retorna 201.

DELETE

El requerimiento DELETE hace exactamente lo contrario a PUT, osea borra el recurso indicado en la URL.  

TRACE

 Este requerimiento sirve para ver como se modificaron los headers desde el momento que partió el requerimiento hasta que llego a la máquina final.

Que es lo que provoca esto en el browser ?

El web browser envia estos requerimientos al clikear un link, enviar un formulario, enviar un archivo, etc. En síntesis, cualquier cosa que haga en el browser provocará que el web server invoque a uno de estos métodos en el servlet (si es que el URI apunta a un servlet obviamente).

Como traduce el web server estos requerimientos en invocaciones?

Para cada uno de estos métodos, existe un método en javax.servlet.http.HttpServlet.

GET - doGet()

POST - doPost()

HEAD - doHead()

OPTIONS - doOptions()

PUT - doPut()

DELETE - doDelete()

Nosotros, como programadores de servlets vamos a extender javax.servlet.http.HttpServlet, sobreescribiendo los métodos en los que estamos interesados.

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.

Cuando llega un requerimiento del cliente, el web server invoca al método service() de HttpServlet con 2 objetos: un HttpServletRequest y un HttpServletResponse. El objeto HttpServletRequest sirve para obtener toda la información concerniente con el requerimiento, es decir los parámetros enviados, los headers, los cookies, etc. Como toda la especificación esta pensada para que no solo sirva para el protocolo HTTP, HttpServletRequest y HttpServletResponse son derivadas de ServletRequest y ServletResponse. Esto hace que el estudio para el SCWCD se complique un poquito, porque tenemos que aprender que métodos pertenecen a ServletRequest y que métodos pertenecen a HttpServletRequest - es decir que métodos son especificos para el protocolo HTTP - .Pensandolo un poquito, nos podremos dar cuenta que los headers y las cookies son objetos que pertenecen especificamente al mundo de HTTP, por lo que pertenecen a HttpServletRequest. Los parámetros, aunque yo personalmente no conozca otros protocolos, pertenecen a la clase general, es decir a ServletRequest.  

Los siguientes métodos son usados para obtener la información de los parámetros (Los métodos para obtener parametros se encuentran en ServletRequest, no en HttpServletRequest!!). Una de las cosas que es importante recordar con los parámetros es que un parámetro puede contener más de un valor. Es decir que para el parámetro nombre, puede tener valores Jose, Carlos, etc. Esto determina un poco la semántica de los diferentes métodos. El segundo a punto a recordar es que los parámetros pueden venir en la URL (cuando se utilizó el método GET) o en el body (cuando se utilizó el método POST):

  • El primer método es getParameter que retornará el primer parámetro. La síntaxis es la siguiente:

 java.lang.String getParameter(java.lang.String name)
          Returns the value of a request parameter as a String, or null if the parameter does not exist.

  • Puede haber más de un valor para un parámetro. El método anterior retornará solamente el 'primer' parámetro. Para obtener todos los valores, se utiliza la función getParameterValues que retorna un array de String con todos los parámetros. La síntaxis es la siguiente:

java.lang.String[] getParameterValues(java.lang.String name)
          Returns an array of String objects containing all of the values the given request parameter has, or null if the parameter does not exist.

  • Podemos obtener todos los parámetros recibidos, juntamente con todos sus valores en un Map (donde cada MapEntry es [parámetro --> Array of String]) a través de la función getParameterMap. La síntaxis es la siguiente:

java.util.Map getParameterMap()
          Returns a java.util.Map of the parameters of this request.

  • Finalmente, si queremos obtener todos los nombres de los parámetros recibidos, utilizamos el método getParameterNames. La síntaxis es la siguiente:

java.util.Enumeration getParameterNames()
          Returns an Enumeration of String objects containing the names of the parameters contained in this request.

Si el contenido del parámetro es binario, por ejemplo cuando se manda un archivo de esta manera:

<form action="/myapp/MiServlet" method="POST">
    <input type="file" name="nombre">
    <input type="submit" value="POST">
</form>

Tenemos 2 métodos para recuperar el parámetro:

 ServletInputStream getInputStream()
          Retrieves the body of the request as binary data using a ServletInputStream.

 

java.io.BufferedReader getReader()
          Retrieves the body of the request as character data using a BufferedReader.

Los siguientes métodos son usados para obtener los headers(** Los headers son un mecanismo de HTTP, por lo que están definidos en HttpServletRequest). Para los headers, como para los parámetros la cantidad de valores puede ser más de una.

  • Para obtener el primer header, usamos un método que sigue el pattern de getParameter llamado getHeader. La síntaxis es la siguiente:

java.lang.String getHeader(java.lang.String name)
          Returns the value of the specified request header as a String.

  • Para obtener todos los valores, utilizamos el método getHeaders. Notar que el pattern es completamente diferente al caso de los parámetros, tanto en el nombre del método como en el tipo retornado que en este caso es una Enumeration. La síntaxis es la siguiente:

java.util.Enumeration getHeaders(java.lang.String name)
          Returns all the values of the specified request header as an Enumeration of String objects.

  • Si queremos obtener los nombres de todos los headers recibidos, utilizamos la función getHeaderNames. Es importante notar que si no existe ningún header (cosa muy poco probable), retorna una Enumeration vacia (no null). La síntaxis es la siguiente:

java.util.Enumeration getHeaderNames()
          Returns an enumeration of all the header names this request contains.

Este método retorna los nombres de todos los headers recibidos.

  • Finalmente, tenemos a nuestra disposición 2 helpers methods que podemos usar cuando sabemos que el resultado es un entero o una fecha.

 long getDateHeader(java.lang.String name)
          Returns the value of the specified request header as a long value that represents a Date object.

 int getIntHeader(java.lang.String name)
          Returns the value of the specified request header as an int.

Obtención de los cookies

  • Para obtener las cookies, utilizamos el método getCookies(** este método tambien se encuentra en HttpServletRequest)

 Cookie[] getCookies()
          Returns an array containing all of the Cookie objects the client sent with this 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.

A través de HttpServletResponse podemos setear los diferentes headers que serán enviados en la respuesta. Como ejemplos de lo que se puede configurar a través de los headers, esta el 'Age' si fue retornado del cache, la ubicación si se quiere hacer un redirect, información del web server, el método de autenticación, etc.

Para agregar headers, tenemos 4 métodos. Los primeros 2 son generales:

  • El primero de los métodos agrega un valor al header

 void addHeader(java.lang.String name, java.lang.String value)
          Adds a response header with the given name and value.

  • Este método reemplaza el valor del header por otro (si existiera más de un valor para el header, la API no especifica nada por lo que supongo que no debería ser usado)

 void setHeader(java.lang.String name, java.lang.String value)
          Sets a response header with the given name and value.

  • Los otros 2 métodos se correponden directamente con los del request y son usados cuando los valores son un entero o una fecha:

 void addIntHeader(java.lang.String name, int value)
          Adds a response header with the given name and integer value.

 void addDateHeader(java.lang.String name, long date)
          Adds a response header with the given name and date-value.

 

Para setear el content type, tenemos 2 maneras:

1. Seteando el header que corresponde:

       HttpServletResponse.setHeader("Content-Type","text/html");

2. Utilizando el método setContentType de ServletResponse:

void setContentType(java.lang.String type)
          Sets the content type of the response being sent to the client, if the response has not been committed yet.

El parámetro type debe contener alguno de los tipos MIME reconocidos por el browser.

 

Para mandar texto al cliente, utilizamos el seguramente conocido método getWriter() de ServletResponse:

java.io.PrintWriter getWriter()
          Returns a PrintWriter object that can send character text to the client.

El texto debe ser HTML bien formado (si, es medio incomodo producirlo desde aquí, y esta es una de las razones del nacimiento de los JSPs). Todo el texto que vayamos enviando se va a almacenar en un buffer, hasta que este se llene y el web server haga el commit hacia el cliente.

Para mandar contenido binario al cliente, utilizamos el método getOutputStream() de ServletResponse:

 ServletOutputStream getOutputStream()
          Returns a ServletOutputStream suitable for writing binary data in the response.

** No se pueden obtener writers de los 2 tipos para enviar una respuesta mezclada al cliente (como hacer esto es descripto en la API de ServletResponse).

Para redireccionar al cliente, utilizamos el método sendRedirect() de HttpServletResponse:

Recuerde que la redirección actúa de esta manera: el servidor le envia al cliente un status code (que indica al cliente que se debe hacer una redirección) y una nueva URL. El cliente, al recibir estas instrucciones realiza un nuevo request a la nueva dirección. Esta es la sintaxis del método:

void sendRedirect(java.lang.String location)
          Sends a temporary redirect response to the client using the specified redirect location URL.

Finalmente, para agregar cookies a la respuesta utilizamos el método addCookie de HttpServletResponse:

 void addCookie(Cookie cookie)
          Adds the specified cookie to the response.

** Cookie tiene un solo constructor de la forma:

Constructor Summary
Cookie(java.lang.String name, java.lang.String value)
          Constructs a cookie with a specified name and value.

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.

Básicamente, el ciclo de vida de un servlet esta dado en la numeración de este objetivo:

(1) y (2) Obviamente el/los classloades deben encontrar la clase que define el servlet, cargarla e instanciarla antes que el servlet pueda servir algun requerimiento.

(3) Todo el código de inicialización debería ser escrito en este método, ya que se asegura que será invocado una sola vez antes que el servlet comienze a servir requerimientos. Además, este método debe concluir exitosamente para que el servlet comienze a servir requerimientos.

** En realidad el webserver no invoca el método init() sino que invoca a init(ServletConfig) que se encuentra definido en GenericServlet. Este método invoca a init() - el método de su servlet será invocado polimorficamente -.

(4) El método service() es invocado por cada uno de los requerimientos que el cliente realiza. Si existiera más d eun requerimiento, el web server crea un thread diferente para cada uno de estos.

(5) El método destroy() es llamado cuando el servlet va a ser sacado de circulación. Para que este método sea llamado se deben cumplir 2 condiciones:

1. El método init() debe haber concluido exitosamente.

2. Todos los threads sirviendo requerimientos deben haber concluido.