Servlets y JSP

Una respuesta desde un servidor Web normalmente consiste en una l�nea de estado, una o m�s cabeceras de respuesta, una l�nea en blanco, y el documento. Seleccionar las cabeceras de respuesta normalmente va mano con mano con la selecci�n de c�digos de estado en la l�nea de estado. Por ejemplo, muchos de los c�digos de estado "document moved" tienen una cabecera Location de acompa�amiento, y un 401 (Unauthorized) debe incluir una cabecera WWW-Authenticate.

Sin embargo, especificar las cabeceras puede jugar un rol muy �til cuando se selecciona c�digos de estado no usuales. Las cabeceras de respuesta se pueden usar para especificar cookies, para suministrar la fecha de modificaci�n (para el cach�), para instruir al navegador sobre la recarga de la p�gina despu�s de un intervalo designado, para decir cuanto tiempo va a estar el fichero usando conexiones persisitentes, y otras muchas tareas.

La forma m�s general de especificar cabeceras es mediante el m�todo setHeader de HttpServletResponse, que toma dos strings: el nombre de la cabecera y el valor de �sta. Igual que la selecci�n de los c�digos de estado, esto debe hacerse antes de env�ar cualquier documento.

Hay tambi�n dos m�todos especializados para seleccionar cabeceras que contienen fechas (setDateHeader) y enteros (setIntHeader). La primera nos evita el problema de tener que traducir una fecha Java en milisegundos (como al devuelta por los m�todos System.currentTimeMillis o getTime aplicados a un objeto Date) en string GMT. El segundo nos ahora la incoveniencia menor de convertir un int a un String.

En el caso de una cabecera cuyo nombre ya exista, podemos a�adir una nueva cabecera en vez de seleccionarla de nuevo. Usamos addHeader, addDateHeader, y addIntHeader para esto. Si realmente nos importa si una cabecera espec�fica se ha seleccionado, podemos usar containsHeader para comprobarlo.

Finalmente, HttpServletResponse tambi�n suministra unos m�todos de conveniencia para especificar cabeceras comunes:

  • El m�todo setContentType selecciona la cabecera Content-Type, y se usa en la mayor�a de los Servlets.
  • El m�todo setContentLength selecciona la cabecera Content-Length, �til si el navegador soporta conexiones HTTP persistentes (keep-alive).
  • El m�todoaddCookie selecciona un cookie (no existe el correspondiente setCookie, ya que es normal que haya varias l�neas Set-Cookie).
  • Y como se especific� en la p�gina anterior, el m�todo sendRedirect selecciona la cabecera Location as� como se selecciona el c�digo de estado 302.

.�Cabeceras de Respuesta m�s Comunes y sus Significados

Cabecera Interpretaci�n/Prop�sito
Allow �Qu� m�todos de petici�n (GET, POST, etc.) soporta el servidor?
Content-Encoding �Qu� m�todo se utiliz� para codificar el documento? Necesitamos decodificarlo para obtener el tipo especificado por la cabecera Content-Type.
Content-Length �Cu�ntos bytes han sido enviados? Esta informaci�n es s�lo necesaria si el navegador est� usando conexiones persistentes. Si queremos aprovecharnos de esto cuando el navegador lo soporte, nuestro servlet deber�a escribir el documento en un ByteArrayOutputStream, preguntar su tama�o cuando se haya terminado, ponerlo en el campo Content-Length, luego enviar el contenido mediante byteArrayStream.writeTo(response.getOutputStream()).
Content-Type �Cu�l es el tipo MIME del siguiente documento? Por defecto en los servlets es text/plain, pero normalmente especifican expl�citamente text/html. Seleccionar esta cabecera es tan com�n que hay un m�todo especial en HttpServletResponse para el: setContentType.
Date �Cu�l es la hora actual (en GMT)? Usamos el m�todo setDateHeader para especificar esta cabecera.
Expires �En qu� momento deber�a considerarse el documento como caducado y no se pondr� m�s en el cach�?
Last-Modified �C�ando se modific� el documento por �ltima vez? El cliente puede suministrar una fecha mediante la cabecera de petici�n If-Modified-Since. Esta es tratada como un GET condicional, donde s�lo se devuelven documentos si la fecha Last-Modified es posterior que la fecha especificada. De otra forma se devuelve una l�nea de estado 304 (Not Modified). De nuevo se usa el m�todo setDateHeader para especificar esta cabecera.
Location �D�nde deber�a ir cliente para obtener el documento? Se selecciona indirectamente con un c�digo de estado 302, mediante el m�todo sendRedirect de HttpServletResponse.
Refresh �C�ando (en milisegundos) deber�a perdir el navegador una p�gina actualizada? En lugar de recargar la p�gina actual, podemos especificar otra p�gina a cargar mediante setHeader("Refresh", "5; URL=http://host/path"). Nota: esto se selecciona comunmente mediante <META HTTP-EQUIV="Refresh" CONTENT="5; URL=http://host/path"> en la secci�n HEAD de la p�gina HTML, mejor que una cabecera expl�cita desde el servidor. Esto es porque la recarga o el reenvio autom�tico es algo deseado por los autores de HTML que no tienen accesos a CGI o servlets. Pero esta cabecera significa "Recarga esta p�gina o ve a URL especificada en n segundos". No significa "recarga esta p�gina o ve la URL especificada cada n segundos". Por eso tenemos que enviar una cabecera Refresh cada vez. Nota: esta cabecera no forma parte oficial del HTTP 1.1, pero es una extensi�n soportada por Netspace e Internet Explorer
Server �Qu� servidor soy? Los servlets normalmente no usan esto; lo hace el propio servidor.
Set-Cookie Especifica una Cookie asociada con la p�gina. Los servlets no deber�an usar response.setHeader("Set-Cookie", ...), pero en su lugar usan el m�todo de prop�sito especial addCookie de HttpServletResponse.
WWW-Authenticate �Qu� tipo de autorizaci�n y domino deber�a suministrar el cliente en su cabecera Authorization? Esta cabecera es necesaria en respuestas que tienen una l�nea de estado 401 (Unauthorized). Por ejemplo response.setHeader("WWW-Authenticate", "BASIC realm=\"executives\"").

Para m�s detalles sobre cabeceras HTTP, puedes ver las especificaciones en http://www.w3.org/Protocols/.

.�Ejemplo: Recarga Autom�tica de P�ginas como Cambio de Contenido

Aqu� tenemos un ejemplo que nos permite pedir una lista de grandes n�meros primos. Como esto podr�a tardar alg�n tiempo para n�meros muy largos (por ejemplo 150 d�gitos), el servelt devuelve los resultados hasta donde haya llegado, pero sigue calculando, usando un thread de baja prioridad para que no degrade el rendimiento del servidor Web. Si los c�lculos no se han completado, instruye al navegador para que pida una nueva p�gina en unos pocos segundos enviando una cabecera Refresh.

Adem�s de ilustrar el valor de las cabeceras de respuesta HTTP, este ejemplo muestra otras dos capacidades de los servlets. Primero, muestra que el mismo servlet puede manejar m�ltiples conexiones simult�neas, cada una con su propio thread. Por eso, mientras un thread est� finalizando los c�lculos para un cliente, otro cliente puede conectarse y todav�a ver resultados parciales.

Segundo, este ejemplo muestra lo f�cil que es para los servlets mantener el estado entre llamadas, algo que es engorroso de implementar en CGI tradicional y sus alternativas. S�lo se crea un ejemplar del Servlet, y cada petici�n siemplemente resulta en un nuevo thread que llama al m�todo service del servlet (que a su vez llama a doGet o doPost). Por eso los datos compartidos s�lo tienen que ser situados en una variable normal de ejemplar (campo) del servlet. As� el servlet puede acceder la c�lculo de salida apropiado cuando el navegador recarga la p�gina y puede mantener una lista de los resultados de las N solicitudes m�s recientes, retorn�ndolas inmediatamente si una nueva solicitud especifica los mismo par�metros que otra reciente. Por supuesto, que se aplican las mismas reglas para sincronizar el acceso multi-thread a datos compartidos.

Los servlets tambi�n pueden almacenar datos persistentes en el objeto ServletContext que est� disponible a trav�s del m�todo getServletContext. ServletContext tiene m�todos setAttribute y getAttribute que nos permiten almacenar datos arbitrarios asociados con claves especificadas. La diferencia entre almacenar los datos en variables de ejemplar y almacenarlos en el ServletContext es que �ste es compartido por todos los servlets en el motor servlet (o en la aplicaci�n Web, si nuestro servidor soporta dicha capacidad).

.�PrimeNumbers.java

Puede descargar el c�digo fuente.

Nota: Tambi�n usa ServletUtilities.java, mostrado anteriormente, PrimeList.java para crear un vector de n�meros primos en un thread de segundo plano, y Primes.java para generar grandes n�meros aleatorios del tipo BigInteger y chequear si son primos:


package hall;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

public class PrimeNumbers extends HttpServlet {
  private static Vector primeListVector = new Vector();
  private static int maxPrimeLists = 30;
  
  public void doGet(HttpServletRequest request,
                    HttpServletResponse response)
      throws ServletException, IOException {
    int numPrimes =
      ServletUtilities.getIntParameter(request, "numPrimes", 50);
    int numDigits =
      ServletUtilities.getIntParameter(request, "numDigits", 120);
    PrimeList primeList =
      findPrimeList(primeListVector, numPrimes, numDigits);
    if (primeList == null) {
      primeList = new PrimeList(numPrimes, numDigits, true);
      synchronized(primeListVector) {
        if (primeListVector.size() >= maxPrimeLists)
          primeListVector.removeElementAt(0);
        primeListVector.addElement(primeList);
      }
    }
    Vector currentPrimes = primeList.getPrimes();
    int numCurrentPrimes = currentPrimes.size();
    int numPrimesRemaining = (numPrimes - numCurrentPrimes);
    boolean isLastResult = (numPrimesRemaining == 0);
    if (!isLastResult) {
      response.setHeader("Refresh", "5");
    }
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    String title = "Some " + numDigits + "-Digit Prime Numbers";
    out.println(ServletUtilities.headWithTitle(title) +
                "<BODY BGCOLOR=\"#FDF5E6\">\n" +
                "<H2 ALIGN=CENTER>" + title + "</H2>\n" +
                "<H3>Primes found with " + numDigits +
                " or more digits: " + numCurrentPrimes + ".</H3>");
    if (isLastResult)
      out.println("<B>Done searching.</B>");
    else
      out.println("<B>Still looking for " + numPrimesRemaining +
                  " more<BLINK>...</BLINK></B>");
    out.println("<OL>");
    for(int i=0; i<numCurrentPrimes; i++) {
      out.println("  <LI>" + currentPrimes.elementAt(i));
    }
    out.println("</OL>");
    out.println("</BODY></HTML>");
  }

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

  // See if there is an existing ongoing or completed calculation with
  // the same number of primes and length of prime. If so, return
  // those results instead of starting a new background thread. Keep
  // this list small so that the Web server doesn't use too much memory.
  // Synchronize access to the list since there may be multiple simultaneous
  // requests.
  
  private PrimeList findPrimeList(Vector primeListVector,
                                  int numPrimes,
                                  int numDigits) {
    synchronized(primeListVector) {
      for(int i=0; i<primeListVector.size(); i++) {
        PrimeList primes = (PrimeList)primeListVector.elementAt(i);
        if ((numPrimes == primes.numPrimes()) &&
            (numDigits == primes.numDigits()))
          return(primes);
      }
      return(null);
    }
  }
}

.�PrimeNumbers.html

Nota: pulsa con el bot�n derecho sobre el enlace al c�digo fuente.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
  <TITLE>Finding Large Prime Numbers</TITLE>
</HEAD>

<BODY BGCOLOR="#FDF5E6">
<H2 ALIGN="CENTER">Finding Large Prime Numbers</H2>
<BR><BR>
<CENTER>
<FORM ACTION="/servlet/hall.PrimeNumbers">
  <B>Number of primes to calculate:</B>
  <INPUT TYPE="TEXT" NAME="numPrimes" VALUE=25 SIZE=4><BR>
  <B>Number of digits:</B>
  <INPUT TYPE="TEXT" NAME="numDigits" VALUE=150 SIZE=3><BR>
  <INPUT TYPE="SUBMIT" VALUE="Start Calculating">
</FORM>
</CENTER>

</BODY>
</HTML>

.�Inicio

Inico

.�Resultados intermedios

Resultados intermedios

.�Resultado Final

Resultados final

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR