Desplegar Servlets y Aplicaciones Web en Tomcat y WebLogic Server

En este artículo revisaremos los pasos implicados en el despliegue de un servlet, describe cómo tomar un servlet y crear una aplicación Web - tanto en formato expandido como en un WAR. Ilustra cómo desplegar una aplicación Web en Apache Tomcat y en WebLogic Server 6.0, un completo servidor de aplicaciones J2EE.

Empezaremos con una breve recapitulación sobre los fundamentos del desarrollo de Servlets, luego mostraremos como construir una aplicación Web para contenerlos. Explicaremos el uso de Web Application Archives (WARs), y luego veremos como desplegar una aplicación web en los entornos anteriormente nombrados.

Desarrollo de Servlets

Los servlets fueron diseñados para permitir la extensión de un servidor proporcionando cualquier servicio. Sin embargo, actualmente sólo se soportan HTTP y páginas JSP. En el futuro, un desarrollador podría extender un servidor FTP o un servidor SMTP usando servlets.

Servlets Genéricos

Un servlet amplía las funcionalidades de un servidor ofreciendo un servicio específico dentro de un marco de trabajo bien definido. Es una pequeña pieza de código Java - normalmente una sóla clase -- que porporciona un servicio específico. Por ejemplo, un servlet HTTP podría proporciona a un cliente de un banco los detalles de sus depositos y reintegros recientes. Otro servlet HTTP podría permitir a un cliente, ver, e incluso editar su dirección de correo.

Para desplegar un servlet, normalmente se requiere la configuración de un servidor de aplicaciones. Cuando el servidor encuentra un tipo particular de solicitud, invoca al servlet, pasándole los detalles sobre la solicitud y un objeto response para devolver el resultado.

Todos lo servlets implementan el interface javax.servlet.Servlet bien directamente -- en el caso de los servelts genéricos -- o indirectamente, en el caso de los servlets HTTP o JSP. El interface javax.servlet.Servlet incluye los siguientes métodos importantes:

  • init():
    Define cualquier código de inicialización que debería ejecutarse cuando se carga el servlet en memoria.
  • service():
    El método principal, llamado cuando el servlet recibe una solicitud de servicio. Define un paquete de lógica de procesamiento proporcionado por el servlet.
  • destroy():
    Define cualquier código de limpieza requerido antes de eliminar el servlet de la memoria.

Cuando el contenedor servlet carga por primera vez un servlet invoca al método init() del servlet para inicializarlo. Luego según se hacen solicitudes para ejecutar el servlet, el contenedor servlet llama repetidamente la método service() del servlet. Finalmente, cuando el contenedor servlet no necesita el servlet, llama al método destroy() del servlet y lo descarga de la memoria. Observa que durante el tiempo de vida de un simple ejemplar servlet, los métodos init() y destroy() sólo son invocados una vez, mientras que el método service() será invocado muchas veces -- una cada vez que se haga una solicitud para ejecutar el servlet.

Servlets HTTP

Los servlets HTTP extienden la clase javax.servlet.http.HttpServlet. Esta clase extiende la clase javax.servlet.GenericServlet, que a su vez implementa javax.servlet.Servlet. La clase HttpServlet sobreescribe el método service() de forma que puede manejar diferentes tipos de solicitudes HTTP: DELETE, GET, OPTIONS, POST, PUT, y TRACE. Por cada uno de estos tipos de solicitud, la clase HttpServlet proporciona su correspondiente método doXXX().

Aunque podemos sobreescribir el método service() en nuestra clase servlet, raramente hay alguna necesidad de hacerlo. Más frecuentemente querremos sobreescribir métodos doXXX() individuales. Si sobreescribimos el método service(), debemos tener cuidado de que los métodos doXXX() por defecto, sólo sean llamados si llamamos a super.service o los invocamos directamente.

Para la mayoría de las aplicaciones querremos sobreescribir los métodos doPost() y doGet(), ya que ellos manejan normalmente los datos enviados por un formulario de usuario desde un FORM HTML.

Para sumarizar, cuando escribamos nuestros servlets HTTP, deberíamos:

  1. Importar como mínimo las clases servlets:
    • javax.servlet.ServletException
    • javax.servlet.http.HttpServlet
    • javax.servlet.http.HttpServletRequest
    • javax.servlet.http.HttpServletResponse
  2. Hacer la clase public
  3. Hacer que la clase extienda HttpServlet
  4. Sobreescribir los métodos doXXX() apropiados para implementar nuestra lógica de solicitud/respuesta..

Un Servlet de Ejemplo: RequestDetails

En el ejemplo de abajo hemos ilustrado un simple servlet HTTP. La primera línea simplemente define a qué paquete pertenece el servlet. El siguiente bloque de código importa las clases usadas en este servlet. Luego viene la definición de la clase servlet. Como puedes ver, la clase RequestDetails extiende HttpServlet.

El cuerpo de RequestDetails define dos métodos: doGet() y doPost(). El método doGet() define la funcionalidad principal de este servlet. El método doPost()simplemente llama a doGet(). Por lo tanto, el servlet maneja las peticiones GET y POST de la misma forma.

El método doGet() construye una página HTML que contiene detalles sobre la solicitud HTTP enviada al servidor. Observa las dos primeras líneas del método. La primera línea selecciona el tipo de contenido de la respuesta. En general, construiremos una página HTML, en cuyo caso el tipo de contenido debe configurarse como text/html. La segunda línea del método doGet() obtiene una referencia a un stream de salida PrintWriter. Toda la salida a devolver para el cliente se escribe en este stream de salida:

package org.stevengould.javaworld;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* This class provides a simple example of a servlet, and
* illustrates some of the information available from an
* HTTP request.
*/
public class RequestDetails extends HttpServlet {
  /**
   * Handler for all GET requests. We simply dump out the
   * requestheader information, followed by the body of
   * the request.
   * @param request the HTTP request submitted to the
   *        server for processing. It is this object that
   *        contains the details of the requested URL, and
   *        it is the details of this object that we
   *        output as a response.
   * @param response the response object to be used to
   *        send a result back to the client.
   * @exception IOException thrown if a communications
   *        error occurs.
   *@exception ServletException if the GET request could
   *        could not be handled
   */
  public void doGet(HttpServletRequest request, HttpServletResponse response)
                           throws IOException, ServletException {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println("<html>");
    out.println("<head>");
    out.println("<title>Request Details Example</title>");
    out.println("</head>");
    out.println("<body>");

    out.println("<h3>HTTP Request Header</h3>");
    out.println("<table border='1'>");
    out.println(" <tr bgcolor=#e0e0e0>");
    out.println("  <td><strong>Name</strong></td>");
    out.println("  <td><strong>Value</strong></td>");
    out.println(" </tr>");
    Enumeration e = request.getHeaderNames();
    while (e.hasMoreElements()) {
        String name = (String)e.nextElement();
        String value = request.getHeader(name);
        out.println(" <tr>");
        out.println("  <td bgcolor=#e0e0e0>"+name+"</td>");
        out.println("  <td>"+value+"</td>");
        out.println(" </tr>");
    }
    out.println("</table>");

    out.println("<h3>HTTP Request Information</h3>");
    out.println("<table border='1'>");
    out.println(" <tr bgcolor=#e0e0e0>");
    out.println("  <td><strong>Name</strong></td>");
    out.println("  <td><strong>Value</strong></td>");
    out.println(" </tr>");
    out.println(" <tr>");
    out.println("  <td bgcolor=#e0e0e0>Method:</td>");
    out.println("  <td>"+request.getMethod()+"</td>");
    out.println(" </tr>");
    out.println(" <tr>");
    out.println("  <td bgcolor=#e0e0e0>Request URI:</td>");
    out.println("  <td>"+request.getRequestURI()+"</td>");
    out.println(" </tr>");
    out.println(" <tr>");
    out.println("  <td bgcolor=#e0e0e0>Protocol:</td>");
    out.println("  <td>"+request.getProtocol()+"</td>");
    out.println(" </tr>");
    out.println(" <tr>");
    out.println("  <td bgcolor=#e0e0e0>PathInfo:</td>");
    out.println("  <td>"+request.getPathInfo()+"</td>");
    out.println(" </tr>");
    out.println(" <tr>");
    out.println("  <td bgcolor=#e0e0e0>Remote Address:</td>");
    out.println("  <td>"+request.getRemoteAddr()+"</td>");
    out.println(" </tr>");
    out.println("</table>");

    out.println("<hr>");
    Date date = new Date();
    out.println("<p align=center>Page generated on "+date);

    out.println("</body>");
    out.println("</html>");

    out.close();
    }

  /**
   * For POST requests, we will simply perform the same
   *operations as for GET requests. The best way to do this
   * is to simply invoke the doGet() method with the appropriate
   * parameters.
   * @param request the HTTP request submitted to the server
   *        for processing. It is this object that contains
   *        the details of the requested URL, and it is the
   *        details of this object that we output as a
   *        response.
   * @param response the response object to be used to send a
   *        result back to the client.
   */
  public void doPost(HttpServletRequest request, HttpServletResponse response) 
                      throws IOException, ServletException {
    doGet(request, response);
    }
  }

Compilar el Servlet

Como los servlets usan clases de extensión Java (clases que no forman parte del JDK principal) debes asegurarte de seleccionar correctamente el CLASSPATH antes de intentar compilar cualquier servlet. El compilador Java necesita poder encontrar las clases y paquetes javax.servlet.* (que se encuentra en el fichero j2ee.jar en el J2EESDK 1.4). A parte de esto, la compilación se realiza igual que con otro programa Java:

  javac RequestDetails.java

Crear una Aplicación Web

Ahora que hemos creado el servlet, necesitamos pensar en desplegarlo. La especificación Java Servlet 2.2 presentó al menos dos caracterísiticas importantes: una aplicación Web y un archivo de aplicación Web (WAR). de acuerdo a las especificaciones Servlets 2.2:

Nota:
Una aplicación Web es una colección de servlets, páginas HTML, clases, y otros recursos que se pueden empaquetar y ejecutar en varios contenedores de distintos venderores.

Los WARs simplemente son archivos Java de una aplicación Web con una extensión diferente para diferenciarlos de los comunmente usados JARs.

Antes de la especificación Servlet 2.2, era bastante diferente desplegar Servlets entre diferentes contenedores servlets -- anteriormente también llamados motores servlet. La especificación 2.2 estandarizó el despliegue entre contenedores, llevando así la portabilidad del código Java un paso más allá. Veremos el poder de esto más adelante en este artículo, cuando ilustremos la creación de una sencilla aplicación Web que se despliegue tanto en Apache Tomcat como en WebLogic Server sin ninguna modificación o recompilación.

Estructura de Directorios de la Aplicación Web

La especificación Servlet 2.2 define la estructura de directorios para los ficheros de una aplicación Web. El directorio superior -- o directorio raíz -- debería tener el nombre de la aplicación y definirá la raíz de documentos para nuestra aplicación Web. Todos los ficheros debajo de esta raíz pueden servirse al cliente excepto aquellos ficheros que están bajo los directorios especiales META-INF y WEB-INF en el directorio raíz. Todos los ficheros privados -- como los ficheros class de los servlets -- deberían almacenarse bajo el directorio WEB-INF.

En la siguiente figura podemos ver la estructura de directorios de una aplicación Web:

Para crear una aplicación Web, empezamos creando esta estructura de directorio. Toma tu fichero de la clase del servlet compilado y sitúala en el directorio WEB-INF/classes. Si hemos definido que nuestro servlet pertenece a un paquete, debemos seguir las reglas estandar de Java y crear los subdirectorios apropiados para que la JVM puedan encontrar nuestras clases. Por ejemplo, si nuestro servlet está definido en un paquete com.mycompany.myproject, deberíamos crear la siguiente estructura de directorios:

  .../WEB-INF
      |-- 
      classes
          |-- 
          com
              |-- 
              mycompany
                  |-- 
                  myproject

Sitúa tus clases Java en el subdirectorio myproject.

Una alternativa útil para copiar los ficheros de clases al directorio apropiado es configurar nuestro entorno de construcción (un Makefile o IDE) para salvar las clases compiladas directamente en los directorios requeridos. Hacer esto nos ahorrará este paso durante el desarrollo.

Modificar el Descriptor de Despliegue

Ahora deberíamos tener todos nuestros ficheros en su lugar para crear nuestra primera aplicación Web. En este punto, necesitamos realizar otra tarea: actualizar el descriptor de despliegue para registrar nuestros servlets con el contenedor. Para crear fácilmente un descriptor de despliegue, simplemente editamos uno existente. Abajo tenemos un esqueleto de un fichero web.xml:

<?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>

    <!-- Tus definiciones van aquí  -->

</web-app>

Insertamos nuestros descriptores de despliegue de servlets entre las etiquetas <web-app> y </web-app> de este fichero. El descriptor de despliegue de un servlet debe incluir las siguientes etiquetas (en este orden):

  <servlet>
     
    <servlet-name>nombre</servlet-name>
     
    <servlet-class>package.nombre.MiClass</servlet-class>

  </servlet>

También están permitidas antes de la etiqueta de cierre </servlet> varias etiquetas opcionales, que definen las propiedades de ejecución del servlet,. Estas etiquetas definen propiedades como parámetros de inicilización, si el servlet debería o no cargarse en la arranada, los roles de seguidad, y las propiedades de pantalla (incluyendo iconos grandes y pequeños, nombre de pantalla y una descripción).

Hasta ahora, nuestro descriptor de despliegue ha descrito el servlet al contenedor de servlets. Luego, debemos describir cuándo el contenedor de servlets debe invocar al servlet -- nos referimos a esto como mapeo. En otras palabras, debemos describir cómo se mapea una URL al servlet. En el fichero web.xml, las URLs se mapean de esta forma:

  <servlet-mapping>
     
    <servlet-name>nombre</servlet-name>
     
    <url-pattern>pattern</url-pattern>

  </servlet-mapping>

OK, suficiente teoría. Veamos un ejemplo de un descritor de despliegue de una aplicación Real. Abajo podemos ver el fichero web.xml mínimo que describe nuestro servlet de ejemplo RequestDetails:

<?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>RequestDetails</servlet-name>
  <servlet-class>org.stevengould.javaworld.RequestDetails</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>RequestDetails</servlet-name>
  <url-pattern>SampleServlet</url-pattern>
</servlet-mapping>

</web-app>

Como puedes ver en la etiqueta de mapeo, hemos elegido la URL /SampleServlet para mapear nuestro servlet RequestDetails.

Ya está! Hemos creado nuestra primera aplicación Web que contiene un sólo servlet. Ahora deberíamos poder desplegar esta aplicación en cualquier contenedor servlet compatible con la especificación 2.2.

De esta forma sabremos como trabajar con ellas y como desplegar nuestras aplicaciones Web en un modo de desarrollo. Sin embargo, en un entorno de producción, mantener juntos los ficheros relacionados es más conveniente. En la siguiente sección, veremos como crear ficheros archivos de aplicaciones Web (WARs) que hacen exactamente esto.

Crear WARs

Como se mencionó anteriormente, un fichero WAR simplemente es un fichero JAR con la extensión cambiada para reflejar su propósito diferente. Ya hemos visto la estructura de directorios requerida para una aplicación Web. Para crear un fichero WAR, usamos esta misma estructura de directorio.

Para crear un WAR para nuestra aplicación , vamos al directorio raíz que contiene nuestra aplicación Web y tecleamos el siguiente comando:

jar cv0f myWebApp.war . 

Observa el punto obligatorio que hay al final de la línea; le dice al programa jar que archive el directorio actual.

El comando jar anterior creará un fichero WAR llamado myWebApp.war. Luego, veremos como desplegar este fichero WAR en Tomcat 3.2 y en WebLogic Server 6.0.

Desplegar Aplicaciones Web en Tomcat 3.2

Tomcat 3.2 sirve como implementación de referencia para la especificación Java Servlet 2.2. Para el propósito de este artículo hemos asumido que estás usando Tomcat 3.2.1 o posterior.

Para desplegar nuestra aplicación Web en Tomcat, copiamos el directorio raíz de nuestra aplicación -- el que contiene web.xml y sus subidrectorios -- al subdirectorio webapps/ROOT/ de nuestra instalación de Tomcat. Podríamos querer salvar una copia de la aplicación Web por defecto antes de sobreescribirla.

Bajo Unix, por ejemplo, si hemos instalado Tomcat en el directorio /opt/jakarta-tomcat-3.2.1, deberíamos copiar las clases del servlet debajo del directorio:

/opt/jakarta-tomcat-3.2.1/webapps/ROOT/

Si ejecutamos Tomcat bajo Windows y los hemos instalado en el directorio C:Program FilesJakarta-Tomcat-3.2.1, deberíamos copiar las clases del servlet bajo el directorio:

C:Program FilesJakarta-Tomcat-3.2.1webappsROOT

El subdirectorio webapps/ROOT/WEB-INF/classes es el directorio por defecto en el que Tomcat buscará nuestras clases Java. Si hemos definido que nuestros servlets pertenecen a un paquete, deberíamos seguir las reglas estándar de Java y crear los subdirectorios apropiados para que la JVM pueda encontrar nuestras clases, como hicimos anteriormente. Por ejemplo, si definimos nuestro servlet en un paquete com.mycompany.myproject, entonces deberíamos tener la estructura de directorios que se ve en la siguiente figura:

Nuestras clases Java estarán en el subdirectorio myproject.

Esto es todo lo necesario. No hay más configuración posterior. Siguiendo con el ejemplo RequestDetails, intenta copiar los ficheros de la aplicación Web en la aplicación Web por defecto de Tomcat.

Probar el Servlet

Para probar nuestro servlet, arranca el servidor Tomcat, abre tu navegador Web, y escribe una URL con la siguiente forma:

http://{address}:{port}/{servletName}

Donde:

  • address es el nombre o dirección IP de la máquina que está ejecutando Tomcat. Podemos usar localhost si el navegador se está ejecutando sobre la misma máquina que Tomcat.
  • port es el puerto en el que escucha Tomcat. Por defecto, es el puerto 8080.
  • servletName es el nombre del servlet que queremos invocar. Debería corresponder al valor contenido en las etiquetas <url-pattern></url-pattern> del fichero descriptor de despliegue.

Por ejemplo, si Tomcat se está ejecutando en la misma máquina que el navegador y está escuchando el puerto por defecto (8080), podemos probar nuestro servlet de ejemplo RequestDetails (que está mapeado a la URL SampleServlet) abriendo la siguiente URL:

http://localhost:8080/SampleServlet

Observa el poco trabajo necesario para desplegar una aplicación Web. Copiar algunos ficheros y probar. Esta facilidad de uso la hacen posible la especificación Java Servlet 2.2 y el uso de los descriptores de despliegue.

Ahora que hemos visto como desplegar servlets en Tomcat, veamos como desplegar un servlet en WebLogic Server.

Desplegar Aplicaciones Web en WebLogic Server 6.0

Aunque WebLogic Server 5.1 fue la primera versión de WebLogic Server en proporcionar soporte para la especificación Java Servlet 2.2 y aplicaciones Web, WebLogic Server 6.0 tiene algunas mejoras importantes que simplifican el despliegue de aplicaciones Web (tanto en formato expandido como empaquetadas como un fichero WAR).

Desplegar WARs Usando la Consola

Con nuestro ejemplar de WebLogic Server ejecutándose, arrancamos la WebLogic Server Console. Asumiendo una instalación por defecto, la consola se puede traer desde la máquina localhost abriendo la siguiente URL en un navegador Web:

  http://localhost:7001/console

Se nos pedíra el nombre y la password del usuario system antes de permitirnos el paso a la Consola.

Para desplegar nuestro fichero WAR, una vez que hemos accedido a la Consola:

  1. Pulsamos sobre le nodo Web Applications en el panel izquierdo de la Consola.
  2. En el panel derecho, pulsamos "Install a new Web Application..."
  3. Tecleamos el path completo y el nombre de fichero de nuestro WAR, o usamos el botón Browse... para localizarlo.
  4. Pulsamos el botón Upload.

Esto es todo. Si todo fue correctamente, deberías ver nuestra aplicación Web listada bajo Web Applications en el panel izquierdo de la Consola. Podrías necesitar refrescar la vista para que apareciera.

Como alternativa al uso de la WebLogic Server Console, es posible copiar la estructura de directorios completa de la aplicación Web como lo hicimos cuando la desplegamos en Tomcat.

Desplegar Aplicaciones Web Manualmente

Normalmetne cuando uno piensa en hacer las tareas manualmente, o a mano, la reacción automática es esperar que la tarea sea un poquito más complicada que su equivalente automático. En el caso de desplegar aplicaciones Web bajo WebLogic Server 6.0, la aproximación manual es tan facíl, si no más, que utilizar la Consola.

Simplemente copiamos nuestro fichero WAR o la estructura de directorios de nuestra aplicación Web completa al subdirectorio config/mydomain/applications de nuestra distribución de WebLogic Server (donde mydomain es el nombre de nuestro dominio WebLogic Server). Tan pronto como nuestros ficheros hayan sido copiados, WebLogic Server despliega la aplicación Web.

Probar el Servlet

Para probar nuestro servlet, abre tu navegador Web, y escribe una URL con la siguiente forma:

http://{address}:{port}/{servletName}

Donde:

  • address es el nombre o dirección IP de la máquina que está ejecutando WebLogic Server. Podemos usar localhost si el navegador se está ejecutando sobre la misma máquina que WebLogic Server.
  • port es el puerto en el que escucha WebLogic Server. Por defecto, es el puerto 7001.
  • servletName es el nombre del servlet que queremos invocar. Debería corresponder al valor contenido en las etiquetas <url-pattern></url-pattern> del fichero descriptor de despliegue.

Por ejemplo, si WebLogic Server se está ejecutando en la misma máquina que el navegador y está escuchando el puerto por defecto (7001), podemos probar nuestro servlet de ejemplo RequestDetails (que estám mapeado a la URL SampleServlet) abriendo la siguiente URL:

http://localhost:7001/SampleServlet

De nuevo, el despliegue de nuestra aplicación Web o fichero WAR sólo ha requerido que copiemos unos cuantos ficheros y probemos el servlet -- no se necesita configuración.

Reconfigurar Aplicaciones Web

Una vez que hemos desplegado nuestras aplicaciones Web en WebLogic Server, podemos usar la Consola para configurar y reconfigurar la aplicación. Según hagamos los cambios en cualquiera de las configuraciones, los detalles se escribirán automáticamente en el fichero config.xml de WebLogic Server. La próxima vez que lo arranquemos usará este fichero para configurar nuestra aplicación.

COMPARTE ESTE ARTÍCULO

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

SIGUIENTE ARTÍCULO