Download Servlets Pág. 1 de 12 SERVLETS Estructura Básica de un Servlet

Document related concepts
no text concepts found
Transcript
Pág. 1 de 11
Servlets
SERVLETS
Estructura Básica de un Servlet
Tipos de request
GET: Cuando se teclea una URL en el navegador, cuando se pincha en un enlace o cuando
se submite un formulario con el atributo METHOD=”GET” o si no se especifica dicho
atributo.
POST: Cuando se submite un formulario que especifica METHOD=”POST”.
Para ser un servlet, la clase debe heredar de la clase HttpServlet y sobreescribir los métodos
doGet o doPost dependiendo de si los datos son enviados con GET o con POST.
Si queremos que hagan lo mismo, basta con llamar desde un método al otro.
Un ejemplo de servlets sería:
//HolaMundo.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HolaMundo extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
String docType =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transational//EN\">\n";
out.println(docType +
"<HTML>\n" +
"<HEAD><TITLE>Hola Mundo</TITLE></HEAD>\n" +
"<BODY>\n" +
"Hola Mundo" +
"</BODY></HTML>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
Ambos métodos tienen dos argumentos: un HttpServletRequest y un HttpServletResponse.
El HttpServletRequest tiene métodos para obtener información sobre datos de un
formulario, peticiones de cabecera y el nombre del host del cliente. El HttpServletResponse
permite especificar información saliente como códigos de estado (200, 404, etc.), cabeceras de
respuesta, y lo más importante, obtener un PrintWriter para devolver el contenido del
documento al cliente.
Desde doGet y doPost se lanzan dos excepciones que hay que incluir en la declaración.
También hay que importar clases que están en java.io (para PrintWriter, etc.),
javax.servlet (para HttpServlet, etc.) y javax.servlet.http (para HttpServletRequest y
HttpServletResponse).
Los servlets no tienen porqué heredar de HttpServlet. También lo pueden hacer de
GenericServlet (clase padre de la anterior).
Compilar e instalar los Servlet
Lo primero que se necesita es tener incluido en el classpath una referencia a servlet.jar.
Pág. 2 de 11
Servlets
El siguiente paso es decidir donde poner los servlets, y esto varía dependiendo del servidor.
Los siguientes PATHs pertenecen a directorios para grabar servlets cuyo código varía
frecuentemente (por ejemplo cuando estamos probando):
Tomcat 3.0:
dir-instalacion\webpages\WEB-INF\classes
Tomcat 3.2:
dir-instalacion\webapps\ROOT\WEB-INF\classes
Java Web Server de Sun y WebSphere de IBM:
dir-instalacion\servlets
WebLogic de BEA:
dir-instalacion\myserver\servletclasses
NOTA: Hay que añadir la línea:
weblogic.httpd.register.servlets=weblogic.servlet.ServletServlet
al fichero weblogic.properties
JSWDK 1.0.1:
dir-instalacion\webpages\WEB-INF\servlets
Invocar los servlets
Lo más habitual es que estén en la dirección http://localhost/servlet/NombreServlet como
ocurre en Tomcat, aunque muchos servidores permiten registrar los servlets con otros
nombres. En WebLogic, la forma de acceder a nuestros servlets es con la direccion:
http://localhost/servlets/NombreServlet.
Empaquetando servlets
Creación de servlets en paquetes
Se graban los ficheros en un subdirectorio cuyo nombre debe ser igual que el de el
paquete.
Se inserta en el código la línea package nombrePackage.
Ejemplo:
//HolaMundo.java
package nombrePackage;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HolaMundo extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
String docType =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transational//EN\">\n";
out.println(docType +
"<HTML>\n" +
"<HEAD><TITLE>Hola Mundo</TITLE></HEAD>\n" +
"<BODY>\n" +
"Hola Mundo" +
"</BODY></HTML>");
}
Pág. 3 de 11
Servlets
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
Compilación de servlets en paquetes
El fichero java se compila igual que antes, teniendo en cuenta que debe estar dentro de
un subdirectorio con el mismo nombre que el paquete.
Llamada a servlets en paquetes
http://localhost/servlets/nombrePackage.MiJava
o
http://localhost/servlet/nombrePackage.MiJava
dependiendo del servidor que estemos utilizando.
Por ejemplo, para llamar al servlet del ejemplo anterior usando Tomcat, la dirección que
habría que poner en el browser sería:
http://localhost/servlet/nombrePackage.HolaMundo
Ejemplo de utilización de funciones de una clase independiente en un servlet:
//ServletUtilities.java
import javax.servlet.*;
import javax.servlet.http.*;
public class ServletUtilities {
public static final String DOCTYPE =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transational//EN\">\n";
public static String headWithTitle(String title) {
return(DOCTYPE + "\n" +
"<HTML>\n" +
"<HEAD><TITLE>" + title + "</TITLE></HEAD>\n");
}
}
//HolaMundo3.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HolaMundo3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println(ServletUtilities.headWithTitle("Hola Mundo 3") +
"<BODY>\n" +
"<h1>Hola Mundo 3</h1>" +
"</BODY></HTML>");
}
}
Ciclo de vida de un servlet
Cuando un servlet es creado por primera vez, es llamado el método init. A continuación, se
crea un hilo que llama al método sercive. Es este método el que se encarga de llamar al
método doGet, doPost u otro doXxx dependiendo del tipo de petición recibida. Por último,
cuando el servidor decide descargar el servlet, primero llama al método destroy del servlet.
Pág. 4 de 11
Servlets
El método init
Este método se utiliza para inicializaciones que se tienen que hacer una única vez, igual
que ocurre con los applets. El servlet puede ser creado cuando se le invoke desde su
URL o cuando se arraque el servidor, dependiendo de como esté registrado en el
servidor web.
Existen dos versiones de este método: una que no recibe ningún parámetro y otra que
recibe un objeto de la clase ServletConfig. La primera version es utilizada cuando el
servlet no necesita leer configuraciones que varíen de un servidor a otro. La definición
del método sería:
public void init() throws ServletException {
// Codigo de inicialización ...
}
La segunda versión se usa cuando el servlet necesita leer especificaciones del servidor
antes de poder completar la inicialización. Por ejemplo, el servlet podría necesitar
conocer la configuración de una base de datos, ficheros de claves, etc. La definición del
esta segunda versión del método sería:
public void init(ServletConfig configuracion) throws ServletException {
super.init(cofiguracion);
// Codigo de inicialización ...
}
Sobre esta última versión, hay que destacar dos cosas: por un lado, el objeto
ServletConfig tiene un método getInitParameter con el cual se pueden buscar los
parámetros de inicialización del servlet, igual que se hace con el método getParameter
usado en el init de los applet. De esta forma queda transparente dónde están
grabados esos parámetros de inicialización, que varían según el servidor. Por ejemplo,
en Tomcat están en un fichero llamado web.xml, en JSWDK está en
servlets.properties, en Weblogic están en weblogic.properties, etc. Por otro lado,
la primera líena del código anterior (super.init(configuracion)) es una llamada
crítica. El objeto ServletConfig es usado más tarde en el servlet, y el método init de
la superclase registra dónde puede el servlet encontrarlo más tarde.
El método service
Cada vez que el servidor recibe una petición por parte de un servlet, éste crea un nuevo
hilo y llama al método service. Este método chequea el tipo de petición (GET, POST,
PUT, DELETE, etc.) y llama a doGet, doPost, doPut, doDelete, etc., según corresponda.
Si se necesita procesar las peticiones GET y POST de forma idéntica, se podría
sobreescribir el método de la siguiente forma:
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Código del servlet.jar
}
Esto no es una buena idea. Es mejor llamar desde doGet a doPost o viceversa.
Los métodos doGet, doPost y doXxx
Estos son los métodos que contienen realmente la 'chicha' del servlet. El 99% de las
veces sólo habrá que sobreescribir los métodos doGet y/o doPost. Si se quiere, también
se pueden sobreescribir los métodos doDelete para peticiones de tipo DELETE, doPut
para PUT, doOptions para OPTIONS y doTrace para TRACE. De todas formas, se tiene
soporte para OPTIONS y TRACE automáticamente por el método service.
El método destroy
La instancia a un servlet puede ser destruida, por ejemplo, por el administrador del
Servlets
Pág. 5 de 11
servidor o porque lleva mucho tiempo inactivo. Antes de ser destruido el servlet llama
al método destroy. Mediante este método se permite al servlet cerrar conexiones a una
base de datos, para hilos que se estén ejecutando en background, grabar contadores en disco,
etc.
No obstante, hay que tener cuidado, porque si se cae el servidor, este método no se ejecuta y
se pueden quedar conexiones abiertas, etc.
Ejemplo de uso de parámetros de inicialización
Aunque la forma de recuperar los parámetros de inicialización es el mismo independiente del
servidor, la forma de definirlos si que varía, por lo que se recomienda que se usen sólo cuando sean
realmente necesarios. También se recomienda que el número de entradas sea mínimo, a fin de
ahorrar trabajo a la hora de cambiar de servidor. En caso de que haya que recuperar muchos
parámetros, se recomienda que en el fichero de propiedades sólo contenga la localizacion de un
fichero, y que los datos estén realmente en ese fichero.
//ShowMessage.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/**
*
*
*
*
*
*
*
*/
Example using servlet initialization. Here, the message
to print and the number of times the message should be
repeated is taken from the init parameters.
<P>
Taken from Core Servlets and JavaServer Pages
from Prentice Hall and Sun Microsystems Press,
http://www.coreservlets.com/.
&copy; 2000 Marty Hall; may be freely used or adapted.
public class ShowMessage extends HttpServlet {
private String message;
private String defaultMessage = "No message.";
private int repeats = 1;
public void init(ServletConfig config)
throws ServletException {
// Always call super.init
super.init(config);
message = config.getInitParameter("message");
if (message == null) {
message = defaultMessage;
}
try {
String repeatString = config.getInitParameter("repeats");
repeats = Integer.parseInt(repeatString);
} catch(NumberFormatException nfe) {
// NumberFormatException handles case where repeatString
// is null *and* case where it is something in an
// illegal format. Either way, do nothing in catch,
// as the previous value (1) for the repeats field will
// remain valid because the Integer.parseInt throws
// the exception *before* the value gets assigned
// to repeats.
}
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "The ShowMessage Servlet";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=CENTER>" + title + "</H1>");
Servlets
Pág. 6 de 11
for(int i=0; i<repeats; i++) {
out.println(message + "<BR>");
}
out.println("</BODY></HTML>");
}
}
web.xml (para Tomcat)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<servlet>
<servlet-name>
ShowMsg
</servlet-name>
<servlet-class>
ShowMessage
</servlet-class>
<init-param>
<param-name>
message
</param-name>
<param-value>
Shibboleth
</param-value>
</init-param>
<init-param>
<param-name>
repeats
</param-name>
<param-value>
5
</param-value>
</init-param>
</servlet>
</web-app>
Leer datos de un formulario desde un servlet
Para leer datos de un formulario, simplemente llamamos al método getParameter de
HttpServletRequest. Este método se utiliza igual para datos enviados con GET y con POST. Si
un mismo parámetro puede tener varios valores (por ejemplo varios checkbox seleccionados),
debemos utilizar getParameterValues (que devuelve un array de strings). El valor devuelto es
null si no existe el nombre del parámetro (igual que getParameter). OJO: los nombres de los
parámetros son sensibles a las mayúsculas y minúsculas.
Para recuperar el nombre de los parámetros, utilizaremos el método getParameterNames. Este
método se suele usar para depurar el código. Estos nombres son recuperados sin ningún orden
en particular dentro de un objeto enumerado.
Ejemplo: Lectura de 2 parámetros explícitos
Este ejemplo recupera dos parámetros que pasamos en la llamada al servlet. Los parámetros
se llaman param1 y param2.
Estos parámetros son tratados por el servlet de la misma manera que si se recuperasen de un
formulario cuyo atributo method fuese igual a GET o si dicho atributo no estuviese definido.
Servlets
Pág. 7 de 11
//DosParametros.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class DosParametros extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Lectura de 2 par&aacute;metros expl&iacute;citos";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY>\n" +
"<H1 ALIGN=CENTER>" + title + "</H1>\n" +
"<UL>\n" +
" <LI><B>param1</B>: "
+ request.getParameter("param1") + "\n" +
" <LI><B>param2</B>: "
+ request.getParameter("param2") + "\n" +
"</UL>\n" +
"</BODY></HTML>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
Estos parámetros los podríamos estar recuperando de un formulario como el siguiente:
<!--FormTresParametros.htm-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Formulario con tres campos</TITLE>
</HEAD>
<BODY>
<H1 ALIGN="CENTER">Formulario con tres campos</H1>
<FORM ACTION="/servlet/DosParametros" method="POST">
Primer Parametro: <INPUT TYPE="TEXT" NAME="param1"><BR>
Segundo Parametro: <INPUT TYPE="TEXT" NAME="param2"><BR>
Tercer Parametro: <INPUT TYPE="TEXT" NAME="param3"><BR>
<CENTER>
<INPUT TYPE="SUBMIT">
</CENTER>
Servlets
</FORM>
</BODY>
</HTML>
Si queremos mostrar todos los parámetros, podemos llamar al siguiente servlet:
//ShowParameters.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
public class ShowParameters extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Reading All Request Parameters";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY>\n" +
"<H1 ALIGN=CENTER>" + title + "</H1>\n" +
"<TABLE BORDER=1 ALIGN=CENTER>\n" +
"<TR BGCOLOR=\"#FFAD00\">\n" +
"<TH>Parameter Name<TH>Parameter Value(s)");
Enumeration paramNames = request.getParameterNames();
while(paramNames.hasMoreElements()) {
String paramName = (String)paramNames.nextElement();
out.print("<TR><TD>" + paramName + "\n<TD>");
String[] paramValues =
request.getParameterValues(paramName);
if (paramValues.length == 1) {
String paramValue = paramValues[0];
if (paramValue.length() == 0)
out.println("<I>No Value</I>");
else
out.println(paramValue);
} else {
out.println("<UL>");
for(int i=0; i<paramValues.length; i++) {
out.println("<LI>" + paramValues[i]);
}
out.println("</UL>");
}
}
out.println("</TABLE>\n</BODY></HTML>");
}
Pág. 8 de 11
Servlets
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
Ejemplo: Restricción de acceso a páginas web
//ProtectedPage.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Properties;
import sun.misc.BASE64Decoder;
public class ProtectedPage extends HttpServlet {
private Properties passwords;
private String passwordFile;
/** Read the password file from the location specified
* by the passwordFile initialization parameter.
*/
public void init(ServletConfig config)
throws ServletException {
super.init(config);
try {
passwordFile = config.getInitParameter("passwordFile");
passwords = new Properties();
passwords.load(new FileInputStream(passwordFile));
} catch(IOException ioe) {}
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String authorization = request.getHeader("Authorization");
if (authorization == null) {
askForPassword(response);
} else {
String userInfo = authorization.substring(6).trim();
BASE64Decoder decoder = new BASE64Decoder();
String nameAndPassword =
new String(decoder.decodeBuffer(userInfo));
int index = nameAndPassword.indexOf(":");
String user = nameAndPassword.substring(0, index);
Pág. 9 de 11
Servlets
String password = nameAndPassword.substring(index+1);
String realPassword = passwords.getProperty(user);
if ((realPassword != null) &&
(realPassword.equals(password))) {
String title = "Welcome to the Protected Page";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=CENTER>" + title + "</H1>\n" +
"Congratulations. You have accessed a\n" +
"highly proprietary company document.\n" +
"Shred or eat all hardcopies before\n" +
"going to bed tonight.\n" +
"</BODY></HTML>");
} else {
askForPassword(response);
}
}
}
// If no Authorization header was supplied in the request.
private void askForPassword(HttpServletResponse response) {
response.setStatus(response.SC_UNAUTHORIZED); // Ie 401
response.setHeader("WWW-Authenticate",
"BASIC realm=\"privileged-few\"");
}
/** Handle GET and POST identically. */
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
//PasswordBuilder.java
import java.util.*;
import java.io.*;
public class PasswordBuilder {
public static void main(String[] args) throws Exception {
Properties passwords = new Properties();
passwords.put("david", "pereira");
passwords.put("chema", "bruna");
passwords.put("marta", "fuentes");
passwords.put("humberto", "alfaro");
// This location should *not* be Web-accessible.
String passwordFile =
"C:\\jakarta-tomcat-3.2.1\\webapps\\ROOT\\WEB-INF\\passwords.properties";
FileOutputStream out = new FileOutputStream(passwordFile);
// Using JDK 1.1 for portability among all servlet
// engines. In JDK 1.2, use "store" instead of "save"
// to avoid deprecation warnings.
passwords.save(out, "Passwords");
}
}
web.xml (para Tomcat)
<servlet>
<servlet-name>
ProtectedPage
</servlet-name>
<servlet-class>
ProtectedPage
</servlet-class>
<init-param>
Pág. 10 de 11
Servlets
<param-name>
passwordFile
</param-name>
<param-value>
C:\\jakarta-tomcat-3.2.1\\webapps\\ROOT\\WEB-INF\\passwords.properties
</param-value>
</init-param>
</servlet>
Direcciones de interés
API de Servlets
http://java.sun.com/products/servlet/2.2/javadoc/index.html
Para validar la sintaxis de nuestras páginas HTML:
http://validator.w3c.org
http://www.htmlhelp.com/tools/validator
Alojamiento gratuito de servlets/jsp
http://www.webappcabaret.com
http://www.mycgiserver.com
Tomcat 3.2.1 (jakarta-tomcat-3.2.1.zip)
http://java.sun.com/products/jsp/tomcat/
http://jakarta.apache.org/builds/jakarta-tomcat/release/v3.2.1/bin/
http://www.51jsp.com/download/tomcat/
Foro de servlets/jsp en castellano
http://www.elistas.net/lista/servlet-jsp
Pág. 11 de 11