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/. © 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ámetros explí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