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

�Resultados intermedios

�Resultado Final
