1. Introducción a las aplicaciones web

Photo by Luca Bravo on Unsplash

1. Introducción a las aplicaciones web

·

37 min read

Table of contents

1.1. ¿Qué es una aplicación web?

Una aplicación web es una aplicación a la que accedemos mediante protocolo HTTP utilizando un navegador web.

El protocolo HTTP especifica el modo de comunicación entre una máquina cliente y una máquina servidor, de modo que el cliente solicita un documento del espacio de direcciones del servidor, y éste se lo sirve.

1.1.1. Aplicaciones en el lado del servidor

El servidor HTTP debe ser capaz de ejecutar programas de aplicación que recojan los parámetros de peticiones del cliente, los procesen y devuelvan al servidor un documento que éste pasará a su vez al cliente.

Así, para el cliente, el servidor no habrá hecho nada distinto a lo estipulado en el protocolo HTTP; pero el servidor podrá valerse de herramientas externas para procesar y servir la petición solicitada, pudiendo así no limitarse a servir páginas estáticas, sino utilizar otras aplicaciones (servlets, JSP, PHP, etc) para servir documentos con contenido dinámico.

Los programas de aplicación son típicamente programas que realizan consultas a bases de datos, procesan la información resultante y devuelven la salida al servidor, entre otras tareas.

1.1.2. Aplicaciones en el lado del cliente

Existen muchas tecnologías relacionadas con extensiones del lado del cliente (entendiendo cliente como un navegador que interpreta código HTML). El código HTML es un código estático que sólo permite formatear la apariencia de una página y definir enlaces a otras páginas o URLs. Esto no es suficiente si queremos que el navegador realice funciones más complicadas: validar entradas de formularios, mostrar la evolución del precio de unas acciones, etc.

Para ampliar las funcionalidades del navegador (respetando el protocolo HTTP), se utilizan tecnologías como JavaScript, Applets, Flash, etc. Estas se basan en hacer que el navegador ejecute código que le pasa el servidor, bien embebido en documentos HTML (como es el caso de JavaScript), o bien mediante ficheros compilados multiplataforma (como es el caso de los Applets Java o los ficheros Flash).

1.2. Aplicaciones web Java EE

En este tipo de aplicaciones, los componentes dinámicos que recibirán las peticiones HTTP en el servidor serán los servlets y JSPs. Estos componentes podrán analizar esta petición y utilizar otros componentes Java para realizar las acciones necesarias (beans, EJBs, etc).

1.2.1. Estructura de la aplicación

Una aplicación web JavaEE que utilice servlets o páginas JSP debe tener una estructura de ficheros y directorios determinada:

  • En el directorio raíz de la aplicación se colocan los documentos web HTML o JSP, junto a los recursos que necesiten, como por ejemplo imágenes, hojas de estilo, ficheros de código JavaScript u otros ficheros referenciados desde los documentos web (podemos dividirlas también en directorios si queremos)

  • Colgando del directorio inicial de la aplicación, se tiene un directorio WEB-INF, que contiene la información Web relevante para la aplicación. Esta información se divide en:

    • Fichero descriptor de despliegue de la aplicación: es el fichero descriptor de la aplicación web. Es un fichero XML (llamado web.xml) que contiene información genérica sobre la aplicación. Lo veremos con más detalle más adelante.

    • Subdirectorio classes: en él irán todas las clases Java utilizadas en la aplicación (ficheros .class), es decir, clases externas a la API de Java que se utilicen en las páginas JSP, servlets, etc. Las clases deberán mantener la estructura de paquetes, es decir, si queremos colocar la clase paquete1.subpaquete1.MiClase dentro de classes, se quedará almacenada en el directorio classes/paquete1/subpaquete1/MiClase.

      💡
      Es recomendable utilizar como nombre de paquete nuestra URL al revés, por ejemplo si tenemos la dirección expertojava.org podríamos utilizar para nuestros proyectos paquetes como org.expertojava.web.servlets.
    • Subdirectorio lib: aquí colocaremos las clases Java que estén empaquetadas en ficheros JAR (es decir, colocaremos los ficheros JAR de nuestra aplicación Web, y las librerías ajenas a la API de JDK o de servlets y JSP que se necesiten)

      💡
      Los ficheros .class se separan de los ficheros JAR, colocando los primeros en el directorio classes y los segundos en lib.

Esta estructura estará contenida dentro de algún directorio, que será el directorio correspondiente a la aplicación Web, y que podremos, si lo hacemos convenientemente, copiar en el servidor que nos convenga. Es decir, cualquier servidor Web JavaEE soporta esta estructura en una aplicación Web, sólo tendremos que copiarla en el directorio adecuado de cada servidor.

Cada aplicación web JavaEE es un contexto, una unidad que comprende un conjunto de recursos, clases Java y su configuración. Cuando hablemos de contexto, nos estaremos refiriendo a la aplicación web en conjunto. Por ello utilizaremos indistintamente los términos aplicación web y contexto.

1.2.2. Rutas relativas al contexto

Cada contexto (aplicación web) instalado en el servidor tendrá asociado una ruta para acceder a él desde la web. Por ejemplo, podemos asociar nuestro contexto la ruta /aplic, de forma que accediendo a la siguiente URL:

http://localhost:8080/aplic/index.htm

Estaremos leyendo el recurso /index.htm de nuestro contexto.

Supongamos que tenemos alguna imagen o recurso al que queremos acceder desde otro, en nuestra aplicación Web. Por ejemplo, supongamos que colgando del directorio raíz de la aplicación tenemos la imagen miImagen.jpg dentro de la carpeta imagenes (es decir, imagenes/miImagen.jpg).

Podemos acceder a esta imagen de varias formas, aunque a veces podemos tener problemas con alguna, porque luego el contenedor Web toma la ruta relativa al lugar desde donde queremos cargar la imagen (o recurso, en general). Este problema lo podemos tener accediendo a elementos desde servlets, sobre todo.

Una solución para evitar esto es acceder a todos los elementos de la aplicación a partir de la ruta del contexto. Por ejemplo, si nuestro contexto tiene la ruta /aplic asociada, para acceder a la imagen desde una página HTML, pondríamos:

<img src="/aplic/imagenes/miImagen.jpg">

1.2.3. Empaquetamiento de aplicaciones web en ficheros WAR

Una forma de distribuir aplicaciones Web Java EE es empaquetar toda la aplicación (a partir de su directorio inicial) dentro de un fichero WAR (de forma parecida a como se hace con un TAR o un JAR), y distribuir dicho fichero. Podemos crear un fichero WAR de la misma forma que creamos un JAR, utilizando la herramienta JAR.

Estos ficheros WAR son un estándar de Java EE, por lo que podremos utilizarlos en los diferentes servidores de aplicaciones Java EE existentes.

Por ejemplo, si tenemos en el directorio web/ejemplo los siguientes ficheros:

web/ejemplo/
    index.html
        WEB-INF/
            web.xml
            classes/
                ClaseServlet.class

Para crear la aplicación WAR se siguen los pasos:

  • Crear el WAR colocándonos en dicho directorio web/ejemplo y escribiendo:

      jar cMvf ejemplo.war *
    

    Las opciones c, v y f son para crear el WAR como un JAR comprimido normal. La opción M (mayúscula) es para que no se añada el fichero MANIFEST.

    Es IMPORTANTE destacar que no debe haber subdirectorios desde la raíz de la aplicación, es decir, la estructura del fichero WAR debe ser:

      index.html
      WEB-INF/
          web.xml
          classes/
              ClaseServlet.class
    

    sin ningún subdirectorio previo (ni ejemplo/ ni web/ejemplo/ ni nada por el estilo).

  • Copiar el fichero WAR al servidor web para poner en marcha la aplicación. Veremos esto con detalle para un servidor de aplicaciones concreto en el siguiente apartado.

    💡
    El empaquetamiento en archivos WAR es algo estándar, pero no así el proceso de despliegue, que es dependiente del servidor. No obstante, la mayoría de servidores Java EE funcionan en este aspecto de modo similar: permiten desplegar las aplicaciones desde una consola de administración y también "dejando caer" el fichero en determinado directorio.

1.3. Creación y despliegue de aplicaciones web con IntelliJ y WildFly

Vamos a pasar ahora a ver la forma de crear y desplegar aplicaciones web Java EE con un IDE y un servidor de aplicaciones concreto. Vamos a utilizar el IDE IntelliJ IDEA y el servidor de aplicaciones WildFly (JBoss 8.1).

1.3.1. Creación de un proyecto web con IntelliJ

Empezaremos creando un nuevo proyecto web Java EE desde el IDE IntelliJ. Encontramos dos formas de hacer esto:

  • Crearlo como proyecto web IntelliJ, con New Project > Java Enterprise > Web Application. El proyecto web tendrá ficheros de configuración propios de este IDE.

  • Crearlo como proyecto Maven dentro de IntelliJ, con New Project > Maven, y seleccionando un arquetipo que genere un proyecto web Java EE 7. De esta forma tendremos un proyecto que se podrá construir con Maven fuera del entorno IntelliJ y podrá ser importado por otros IDEs, y que además contará con los ficheros de configuración propios de IntelliJ para poder trabajar con él dentro de este entorno. Cuando se produzcan cambios en la configuración de Maven del proyecto, podremos importar dichos cambios a la configuración del proyecto IntelliJ.

Vamos a utilizar la segunda opción, dada la flexibilidad que nos proporciona Maven para poder construir el proyecto de forma independiente al IDE utilizado.

En la pantalla de creación del proyecto deberemos marcar la casilla Create from archetype, y pulsar sobre el botón Add archetype …​, ya que el arquetipo que vamos a necesitar (aplicación web Java EE 7) no está en la lista por defecto:

Creación de un nuevo proyecto web

Del arquetipo introduciremos los siguientes datos:

Añadir un nuevo arquetipo Maven

Una vez seleccionado el arquetipo deberemos introducir la configuración del módulo Maven (artifactId, groupId, y version):

Configuración del módulo Maven

<groupId>org.codehaus.mojo.archetypes</groupId>
<artifactId>webapp-javaee7</artifactId>
<version>1.1</version>

Tras esto, introduciremos el nombre que va a tener el proyecto dentro de IntelliJ. Esto ya no es configuración de Maven, sino del propio IDE:

Nombre del proyecto IntelliJ

Con esto habremos finalizado, y veremos nuestro nuevo proyecto con la siguiente organización:

Layout del proyecto web en IntelliJ

💡
Es posible que durante la creación del proyecto IntelliJ nos pregunte repetidas veces si queremos recargar la información de Maven en el proyecto. Responderemos siempre que si y podemos indicar que esta información se importe de forma automática cuando haya cambios (Enable Auto-Import).

1.3.2. Despliegue del proyecto en WildFly desde línea de comandos

Podemos desplegar nuestro proyecto en el servidor de aplicaciones WildFly directamente desde línea de comandos, al igual que ocurre con la mayoría de servidores de aplicaciones Java EE.

En primer lugar deberemos poner el servidor WildFly en marcha. Por el momento será suficiente con poner el servidor standalone, que contiene una única instancia del servidor de aplicaciones. Para poner en marcha el servidor ejecutaremos el siguiente comando desde el directorio $WILDFLY_HOME/bin, siendo $WILDFLY_HOME el directorio de instalación de este servidor de aplicaciones:

./standalone.sh

Con el servidor WilfFly en marcha podremos desplegar una aplicación simplemente "dejando caer" el fichero war en el directorio $WILDFLY_HOME/standalone/desployments. Pasados unos segundos desde la copia del fichero la aplicación se habrá desplegado y podremos acceder a ella mediante un navegador. Por ejemplo, si copiamos un fichero miaplicacion.war, por defecto podremos acceder a ella mediante:

http://localhost:8080/miaplicacion/

1.3.3. Despliegue del proyecto en WildFly con IntelliJ

Una vez tenemos un proyecto web creado podemos desplegarlo en un servidor de aplicaciones. Para ello deberemos en primer lugar configurar en IntelliJ el servidor donde queramos desplegar la aplicación, y en segundo lugar crear un perfil de ejecución que le indique a WildFly cómo debe hacer el despliegue de nuestra aplicación web.

Configuración de WildFly en IntelliJ

En primer lugar deberemos configurar el servidor de aplicaciones a utilizar. Para ello entraremos en File > Settings …​ > Application Servers. En dicha sección pulsaremos sobre el botón para añadir un nuevo servidor de tipo JBoss:

Añadir un nuevo servidor JBoss

Nos pedirá el directorio donde está instalado WildFly:

Directorio de instalación de JBoss

Tras introducirlo veremos el servidor JBoss configurado y ya podremos desplegar el proyecto en él:

Servidor JBoss configurado

💡
También se podría configurar el servidor en el momento de la creación del perfil de ejecución.
Creación de un perfil de ejecución

Para poder desplegar nuestra aplicación web deberemos crear un perfil de ejecución para ella dentro de IntelliJ.

💡
Necesitaremos un perfil de ejecución por cada aplicación web que queramos poder desplegar.

En primer lugar seleccionaremos Run > Edit configurations …​. Dentro de esta pantalla pulsaremos el botón + y añadiremos un nuevo perfil de tipo JBoss Server > Local. Veremos un formulario como el siguiente:

Creación de un perfil de ejecución

En el campo Name podemos darle un nombre al nuevo perfil de ejecución. Si ya hemos configurado el servidor JBoss, veremos que ya aparece seleccionado como servidor en el que desplegar. En caso contrario, podríamos configurarlo desde esta pantalla.

Veremos también que en la parte inferior aparece un Warning indicando que no hay seleccionados artefactos para ser desplegados. Deberemos indicar qué artefacto va a ser desplegado en el servidor cuando ejecutemos este perfil. Podemos indicarlo pulsando sobre el botón Fix o pasando de forma manual a la pestaña Deployment. Desde esta pestaña podemos añadir un nuevo artefacto a desplegar pulsando sobre el botón +:

Selección de artefactos

Tenemos dos artefactos configurados por defecto:

  • war: Despliega la aplicación web empaquetada en un fichero WAR

  • war exploded: Despliega la aplicación web publicando el directorio sin empaquetar.

Seleccionaremos uno de ellos y nos aparecerá como artefacto a desplegar:

Artefactos a desplegar

Ahora ya podremos guardar el perfil de ejecución y pulsar el botón Run para realizar el despliegue. A partir de este momento podremos lanzar este perfil en cualquier momento para volver a realizar el despliegue de la aplicación.

Configuración de artefactos

Podemos configurar los artefactos generados por el proyecto en la pantalla File > Project Structure …​ > Artifacts. Aquí veremos los dos artefactos que se crear por defecto para las aplicaciones web, y podremos editarlos o crear nuevos:

Configuración de artefactos

Por ejemplo, podríamos modificar el nombre del fichero WAR para así cambiar el nombre del contexto donde se desplegará la aplicación.

💡
Si queremos cambiar el nombre del contexto en el que se despliega la aplicación es mejor hacerlo introduciendo en el fichero pom-xml el elemento <finalName> dentro de la etiqueta <build>.
<project>
    ...
    <build>
        <finalName>MiAplicacionWeb</finalName>
        ...
    </build>
</project>

Depuración y cambios en caliente

El entorno IntelliJ nos permite realizar cambios en caliente dentro de nuestra aplicación web, es decir, conforme modificamos el código de la aplicación se aplicarán los cambios a la aplicación en ejecución sin necesidad de volver a desplegar. Para poder realizar cambios en caliente deberemos desplegar de la siguiente forma:

  1. Seleccionaremos como artefacto a generar la aplicación web descomprimida (war exploded).

    💡
    Deberemos renombrar el artefacto de tipo war exploded de forma que acabe con .war, ya que de no ser así obtendremos un error en IntelliJ.
  2. Desplegaremos la aplicación en modo debug.

Con esto, cada vez que realicemos un cambio se aplicará a la aplicación ya desplegada en el servidor. Además, la ejecución en modo debug nos permitirá añadir puntos de parada y realizar la ejecución paso a paso inspeccionando el valor de cada variable.

1.4. Componentes de la aplicación Java EE

1.4.1. El descriptor de despliegue

Como hemos dicho anteriormente, el directorio WEB-INF de una aplicación web con servlets y/o páginas JSP, debe haber un fichero descriptor de despliegue (llamado web.xml) que contenga la información relativa a la aplicación. Este descriptor de despliegue es el mecanismo estándar para configurar aplicaciones web JavaEE.

💡
A partir de la API de servlets 3.0 (Java EE 7) el descriptor de despliegue es opcional, pero es recomendable contar con él ya que será necesario para configurar determinados elementos.

El web.xml es estándar en JavaEE y por tanto todo lo visto en esta sección es igualmente aplicable a cualquier servidor compatible JavaEE, aunque no sea WildFly.

Estructura del descriptor de despliegue

Es un fichero XML, que comienza con una cabecera XML que indica la versión y la codificación de caracteres, y un DOCTYPE que indica el tipo de documento, y la especificación de servlets que se sigue. La etiqueta raíz del documento XML es web-app. Así, un ejemplo de fichero podría ser:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  id="WebApp_ID" version="3.0">

    <display-name>
        Mi Aplicacion Web
    </display-name>
    <description>
        Esta es una aplicacion web sencilla a modo de ejemplo
    </description>
</web-app>

En este caso se está utilizando la especificación 3.0 de servlets. Algunos servidores permiten omitir la cabecera xml y el DOCTYPE, pero sí es una buena costumbre el ponerlas.

Dentro de la etiqueta raíz <web-app> podemos colocar otros elementos que ayuden a establecer la configuración de nuestra aplicación web. Veremos a continuación los elementos de configuración general de la aplicación. Conforme veamos cada característica de la API de servlets, repasaremos los elementos del descriptor del despliegue que hagan referencia a ella.

Información general de la aplicación

Tenemos dos etiquetas que nos permiten especificar información sobre la aplicación que se mostrará cuando se presente en una interfaz gráfica, y nos servirán para identificarla:

  • <display-name>: nombre con que deben utilizar las aplicaciones gráficas para referenciar a la aplicación

  • <description>: texto descriptivo de la aplicación

Páginas de inicio

Podemos también indicar la página (o posibles páginas) que se devolverán por defecto si no se especifica ninguna concreta en la URL:

  • <welcome-file-list>: para indicar qué páginas debe buscar el servidor como páginas de inicio en el caso de que en la URL se indique el directorio, pero no la página, como por ejemplo:

http://localhost:8080/unadireccion/dir/

Para ello, esta etiqueta tiene una o varias subetiquetas <welcome-file> para indicar cada una de las posibles páginas

Por ejemplo, podemos indicar que las páginas por defecto sean index.html o index.jsp con:

<welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

Las páginas se buscan en el orden en que se especifican en esta etiqueta.

Creación del descriptor de despliegue con IntelliJ

Al crear un proyecto Java EE 7 (o superior) con IntelliJ nos da la opción de crear o no el descriptor de despliegue. Esto es debido a que, como se ha comentado anteriormente, en esta versión de la plataforma es opcional contar con este fichero. Cuando el proyecto es creado con el arquetipo webapp-javaee7 de Maven el descriptor de despliegue no se crea. A pesar de ser un fichero opcional, si no se ha creado de forma automática al crear el proyecto es recomendable crearlo posteriormente.

En algunas versiones de IntelliJ no funciona correctamente la creación de servlets si no se cuenta con un descriptor de despliegue. Se trata de un bug, ya que no es necesario contar con un descriptor de despligue para crear servlets en Java EE 7.

Para crear el descriptor de despliegue con IntelliJ en un proyecto que todavía no cuenta con él, podemos entrar en File > Project Structure …​_ > Facets, y seleccionar el facet Web. Dentro de esta pantalla pulsaremos el botón + para crear un nuevo descriptor de despliegue en la aplicación. Deberemos indicar el directorio donde crearlo, que al ser un proyecto de Maven deberá ser el siguiente:

Añadir el descriptor de despligue

En el proyecto Maven IntelliJ no detecta correctamente el directorio donde crear el fichero web.xml. Es importante que esté en src/main/webapp/WEB-INF.

Una vez creado, veremos que la aplicación ya cuenta con un Web Module Deployment Description:

Descriptor de despliegue en facet web

1.4.2. Servlets

Un servlet es un programa Java que se ejecuta en un servidor Web y construye o sirve páginas web. De esta forma se pueden construir páginas dinámicas, basadas en diferentes fuentes variables: datos proporcionados por el usuario, fuentes de información variable (páginas de noticias, por ejemplo), o programas que extraigan información de bases de datos.

Comparado con un CGI, un servlet es más sencillo de utilizar, más eficiente (se arranca un hilo por cada petición y no un proceso entero), más potente y portable. Con los servlets podremos, entre otras cosas, procesar, sincronizar y coordinar múltiples peticiones de clientes, reenviar peticiones a otros servlets o a otros servidores, etc.

Recursos de servlets y JSP

Normalmente al hablar de servlets se habla de JSP y viceversa, puesto que ambos conceptos están muy interrelacionados. Para trabajar con ellos se necesitan tener presentes algunos recursos:

  • Un servidor web que dé soporte a servlets / JSP (contenedor de servlets y páginas JSP). Ejemplos de estos servidores son Apache Tomcat, Resin, JRun, Java Web Server, BEA WebLogic, etc.

  • Las librerías (clases) necesarias para trabajar con servlets / JSP. Normalmente vienen en ficheros JAR en un directorio lib del servidor. Al desarrollar nuestra aplicación, deberemos tener las librerías necesarias en el classpath para que compilen los ficheros (sólo necesitaremos compilar los servlets, no los JSP).

  • La documentación sobre la API de servlets / JSP (no necesaria, pero sí recomendable)

    💡
    Cuando trabajemos con un entorno como IntelliJ, al crear un proyecto de aplicación web se añadirán de forma automática referencias a las librerías de componentes web del servidor.

Arquitectura del paquete servlet

Dentro del paquete javax.servlet tenemos toda la infraestructura para poder trabajar con servlets. El elemento central es la interfaz Servlet, que define los métodos para cualquier servlet. La clase GenericServlet es una clase abstracta que implementa dicha interfaz para un servlet genérico, independiente del protocolo. Para definir un servlet que se utilice vía web, se tiene la clase HttpServlet dentro del subpaquete javax.servlet.http. Esta clase hereda de GenericServlet, y también es una clase abstracta, de la que heredaremos para construir los servlets para nuestras aplicaciones web. Cuando un servlet acepta una petición de un cliente, se reciben dos objetos:

  • Un objeto de tipo ServletRequest que contiene los datos de la petición del usuario (toda la información entrante). Con esto se accede a los parámetros pasados por el cliente, el protocolo empleado, etc. Se puede obtener también un objeto ServletInputStream para obtener datos del cliente que realiza la petición. La subclase HttpServletRequest procesa peticiones de tipo HTTP.

  • Un objeto de tipo ServletResponse que contiene (o contendrá) la respuesta del servlet ante la petición (toda la información saliente). Se puede obtener un objeto ServletOutputStream, y un Writer, para poder escribir la respuesta. La clase HttpServletResponse se emplea para respuestas a peticiones HTTP.

Ciclo de vida de un servlet

Todos los servlets tienen el mismo ciclo de vida:

  • Un servidor carga e inicializa el servlet.

  • El servlet procesa cero o más peticiones de clientes. Por cada petición se lanza un hilo, ejecutándose estos hilos de forma concurrente en sobre un mismo objeto servlet.

  • El servidor destruye el servlet, normalmente en momentos en los que no tiene peticiones o cuando se apaga el servidor.

1. Inicialización

En cuanto a la inicialización de un servlet, se tiene una por defecto en el método init().

public void init() throws ServletException
{
  ...
}

public void init(ServletConfig conf) throws ServletException
{
  super.init(conf);
  ...
}

El primer método se utiliza si el servlet no necesita parámetros de configuración externos. El segundo se emplea para tomar dichos parámetros del objeto ServletConfig que se le pasa. La llamada a super.init(…​) al principio del método es MUY importante, porque el servlet utiliza esta configuración en otras zonas. Si queremos definir nuestra propia inicialización, deberemos sobreescribir alguno de estos métodos. Si ocurre algún error al inicializar y el servlet no es capaz de atender peticiones, debemos lanzar una excepción de tipo UnavailableException. Podemos utilizar la inicialización para establecer una conexión con una base de datos (si trabajamos con base de datos), abrir ficheros, o cualquier tarea que se necesite hacer una sola vez antes de que el servlet comience a funcionar.

2. Procesamiento de peticiones

Una vez inicializado, cada petición de usuario lanza un hilo que llama al método service() del servlet.

public void service(HttpServletRequest request,
        HttpServletResponse response)
throws ServletException, IOException

Este método obtiene el tipo de petición que se ha realizado (GET, POST, PUT, DELETE). Dependiendo del tipo de petición que se tenga, se llama luego a uno de los métodos:

  • doGet():
public void doGet(HttpServletRequest request,
      HttpServletResponse response)
throws ServletException, IOException

Para peticiones de tipo GET (aquellas realizadas al escribir una dirección en un navegador, pinchar un enlace o rellenar un formulario que no tenga METHOD=POST)

  • doPost():
public void doPost(HttpServletRequest request,
       HttpServletResponse response)
throws ServletException, IOException

Para peticiones POST (aquellas realizadas al rellenar un formulario que tenga METHOD=POST)

  • doXXX(): normalmente sólo se emplean los dos métodos anteriores, pero se tienen otros métodos para peticiones de tipo DELETE (doDelete()), PUT (doPut()), OPTIONS (doOptions()) y TRACE (doTrace()).

3. Destrucción

El método destroy() de los servlets se emplea para eliminar un servlet y sus recursos asociados.

public void destroy() throws ServletException

Aquí debe deshacerse cualquier elemento que se construyó en la inicialización (cerrar conexiones con bases de datos, cerrar ficheros, etc). El servidor llama a destroy() cuando todas las llamadas de servicios del servlet han concluido, o cuando haya pasado un determinado número de segundos (lo que ocurra primero). Si esperamos que el servlet haga tareas que requieran mucho tiempo, tenemos que asegurarnos de que dichas tareas se completarán. Podemos hacer lo siguiente:

  • Definir un contador de tareas activas, que se incremente cada vez que una tarea comienza (entendemos por tarea cada petición que se realice al servlet), y se decremente cada vez que una termina. Podemos utilizar bloques de código synchronized para evitar problemas de concurrencia.

  • Hacer que el método destroy() no termine hasta que lo hagan todas las tareas pendientes (comprobando el contador de tareas pendientes)

  • Hacer que las tareas pendientes terminen su trabajo si se quiere cerrar el servlet (comprobando algún flag que indique si el servlet se va a cerrar o no).

Estructura básica de un servlet

La plantilla común para implementar un servlet es:

import javax.servlet.*;
import javax.servlet.http.*;

public class ClaseServlet extends HttpServlet {

  public void doGet(HttpServletRequest request,
                    HttpServletResponse response)
               throws ServletException, IOException {
    // ... codigo para una peticion GET
  }

  public void doPost(HttpServletRequest request,
                     HttpServletResponse response)
               throws ServletException, IOException {
    // ... codigo para una peticion POST
  }
}

El servlet hereda de la clase HttpServlet. Normalmente se deben sobreescribir los métodos doGet(), doPost() o ambos, colocando el código que queremos que se ejecute cuando se reciba una petición GET o POST, respectivamente. Conviene definir los dos para distinguir ambas peticiones. En caso de que queramos hacer lo mismo para GET o POST, definimos el código en uno de ellos, y hacemos que el otro lo llame. Aparte de estos métodos, podemos utilizar otros de los que hemos visto: init() (para inicializaciones), doXXX() (para tratar otros tipos de peticiones (PUT, DELETE, etc)), destroy() (para finalizar el servlet), etc, así como nuestros propios métodos internos de la clase.

Configuración de servlets en aplicaciones web

Para instalar un servlet en una aplicación web, se coloca la clase compilada del servlet dentro del directorio WEB-INF/classes de la aplicación (respetando también la estructura de paquetes, creando tantos subdirectorios como sea necesario).

De forma alternativa, también podríamos empaquetar nuestros servlets en un JAR y poner esta librería de clases dentro del directorio WEB-INF/lib. De cualquiera de las dos formas la clase del servlet estará localizable para la aplicación. Veremos ahora las formas que tenemos de invocar a ese servlet.

Mapeo de servlets en el fichero descriptor

Los servlets se invocarán cuando desde un cliente hagamos una petición a una URL determinada. Para especificar la URL a la que está asociada cada servlet, deberemos configurar dicho servlet en el fichero descriptor de despliegue (web.xml).

En primer lugar deberemos introducir una marca <servlet> para declarar cada servlet de la siguiente forma:

<servlet>
  <servlet-name>nombre</servlet-name>
  <servlet-class>unpaquete.ClaseServlet</servlet-class>
</servlet>

Donde <servlet-name> es un nombre identificativo y arbitrario del servlet, y <servlet-class> es la clase del servlet.

💡
Al declarar un servlet, debe indicarse el nombre completo de la clase en la que está implementado, incluyendo paquetes y subpaquetes. Esto es así porque en el web.xml no tenemos ningún "import" que nos permita desambiguar entre posibles diferentes clases con el mismo nombre.

Una vez declarado nuestro servlet, deberemos mapearlo a una URL. Esto se consigue mediante las etiquetas <servlet-mapping>:

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

En la subetiqueta <servlet-name> se pone el nombre del servlet al que se quiere asignar la URL (será uno de los nombres dados en alguna etiqueta <servlet> previa), y en <url-pattern> colocamos la URL que le asignamos al servlet, que debe comenzar con '/'.

💡
Destacamos que primero se colocan todas las etiquetas <servlet>, y luego las <servlet-mapping> que se requieran. En las actuales versiones de los servidores web el orden es indiferente, pero si queremos garantizar la compatibilidad con versiones anteriores, deberemos respetarlo.

Así, con lo anterior, podremos llamar al servlet identificado con nombre accediendo a la URL a la que se encuentra mapeado:

http://localhost:8080/<dir>/ejemploservlet

siendo <dir> el directorio en el que se encuentra el contexto de nuestra aplicación Web.

También podemos asignar en <url-pattern> expresiones como:

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

o como:

<servlet-mapping>
  <servlet-name>nombre</servlet-name>
  <url-pattern>/ejemploservlet/*.do</url-pattern>
</servlet-mapping>

Con el primero, cualquier URL del directorio de nuestra aplicación Web que comience con /ejemploservlet/ se redirigirá y llamará al servlet identificado con nombre. Por ejemplo, las direcciones:

http://localhost:8080/<dir>/ejemploservlet/unapagina.html

http://localhost:8080/<dir>/ejemploservlet/login.do

acabarían llamando al servlet nombre.

Con el segundo, cualquier llamada a un recurso acabado en .do del directorio /ejemploservlet/ de nuestra aplicación se redirigiría al servlet nombre. Podemos hacer que distintas URLs llamen a un mismo servlet, sin más que añadir varios grupos <servlet-mapping>, uno por cada patrón de URL diferente, y todos con el mismo <servlet-name>.

Este mismo procedimiento se puede aplicar también si en lugar de un servlet queremos tratar una página JSP. Para declarar una página JSP, sustituiremos la etiqueta <servlet-class> por la etiqueta <jsp-file>:

<servlet>
  <servlet-name>nombre2</servlet-name>
  <jsp-file>/mipagina.jsp</jsp-file>
</servlet>

Esta página se podrá mapear a diferentes URLs de la misma forma en la que lo hacemos para un servlet.

Mapeo de servlets mediante anotaciones

En la API de Servlets 3.0 se incluyen una serie de importantes novedades dirigidas principalmente a facilitar el desarrollo de los componentes web. Esto se consigue mediante el uso de anotaciones para configurar los servlets, en lugar de tener que declararlos en el fichero web.xml. Esto supone un cambio importante en la forma de trabajar con servlets. Ahora bastará con anotar la clase en la que implementamos el servlet de la siguiente forma:

@WebServlet(name="miServlet", urlPatterns="/UrlServlet")
public class ClaseServlet extends HttpServlet {

    public void doGet(HttpServletRequest request,
                      HttpServletResponse response) {
        ...
    }

    public void doPost(HttpServletRequest request,
                       HttpServletResponse response) {
        ...
    }
}

En la anotación @WebServlet se debe especificar obligatoriamente el atributo urlPatterns, con el que se especifica la URL a la que queremos mapear el servlet. Si no necesitamos más parámetros que la URL, podemos especificarla como valor por defecto de la anotación:

@WebServlet("/UrlServlet")

Con esto ya no se necesitará declarar el servlet en el fichero web.xml.

Asignar parámetros de inicio a un servlet o página JSP

El hecho de asignar un nombre a un servlet o página JSP mediante la etiqueta <servlet> y sus subetiquetas nos permite identificarlo con ese nombre, y también poderle asignar parámetros de inicio. Para asignar parámetros se colocan etiquetas <init-param> dentro de la etiqueta <servlet> del servlet o página JSP al que le queremos asignar parámetros. Dichas etiquetas tienen como subetiquetas un <param-name> (con el nombre del parámetro) y un <param-value> (con el valor del parámetro). Por ejemplo:

<servlet>
  <servlet-name>nombre</servlet-name>
  <servlet-class>ClaseServlet</servlet-class>
  <init-param>
    <param-name>param1</param-name>
    <param-value>valor1</param-value>
  </init-param>
  <init-param>
    <param-name>param2</param-name>
    <param-value>valor2</param-value>
  </init-param>
</servlet>

Si estamos utilizando Servlet 3.0, podemos también utilizar la anotación @WebServlet para configurar los parámetros de inicio:

@WebServlet(urlPatterns="/UrlServlet",
         initParams ={
             @InitParam(name="param1", value="valor1"),
             @InitParam(name="param2", value="valor2") })

Estos parámetros también pueden ser declarados por separado con la anotación @WebInitParam:

@WebInitParam(name="param1", value="valor1")

Para obtener luego los parámetros desde el servlet se utiliza getServletConfig().getInitParameter(nombre) donde nombre es el valor <param-name> del parámetro que se busca, y devuelve el valor (elemento <param-value> asociado), que es de tipo String siempre. Para obtener estos valores desde páginas JSP se emplean otros métodos. Los parámetros de inicio sólo se aplican cuando accedemos al servlet o página JSP a través del nombre asignado en <servlet-name>, o a través de la URL asociada en un <servlet-mapping>.

Cargar servlets al inicio

A veces nos puede interesar que un servlet se cargue al arrancar el servidor, y no con la primera petición de un cliente. Para hacer eso, incluimos una etiqueta <load-on-startup> dentro de la etiqueta <servlet>. Dicha etiqueta puede estar vacía:

<servlet>
  <servlet-name>nombre</servlet-name>
  <servlet-class>ClaseServlet</servlet-class>
  <load-on-startup/>
</servlet>

o contener un número:

<servlet>
  <servlet-name>nombre</servlet-name>
  <servlet-class>ClaseServlet</servlet-class>
  <load-on-startup>2</load-on-startup>
</servlet>

que indica el orden en que el servidor irá cargando los servlets (de menor a mayor valor).

Como en los casos anteriores, esto también puede ser indicado mediante la anotación @WebServlet:

@WebServlet(name="miServlet", urlPatterns="/UrlServlet", loadOnStartup="2")

Logging en aplicaciones Java EE con servlets

Utilizaremos Log4J como librería de logging, pero encapsulada dentro de la librería commons-logging de Jakarta. Para poder imprimir mensajes de log en una aplicación que contenga servlets se deben seguir estos pasos:

  • Añadir los ficheros JAR de las librerías (commons-logging-X.X.jar y log4j-X.X.X.jar) en la carpeta WEB-INF/lib de nuestra aplicación (o añadirlas como dependencias de Maven).

  • Colocar dos ficheros .properties en el CLASSPATH de la aplicación (carpeta WEB-INF/classes):

    • Un fichero commons-logging.properties indicando que vamos a utilizar Log4J como librería subyacente.

    • Un fichero log4j.properties con la configuración de logging para Log4J.

Estos ficheros los colocaremos en una carpeta fuente llamada resources, para que al compilarse la aplicación se vuelquen a /WEB-INF/classes.

  • Finalmente, sólo queda en cada servlet o clase Java colocar los mensajes de log donde queramos. Veremos cómo hacerlo en servlets y páginas JSP en el siguiente módulo. Aquí vemos un ejemplo de cómo ponerlos en cada servlet:
...

import org.apache.commons.logging.*;

public class ServletLog4J1 extends HttpServlet {

  static Log logger = LogFactory.getLog(ServletLog4J1.class);

  // Metodo para procesar una peticion GET

  public void doGet(HttpServletRequest request,
                    HttpServletResponse response)
                        throws ServletException, IOException {
    logger.info("Atendiendo peticion Servlet Log4J");
    PrintWriter out = response.getWriter();
    out.println ("Servlet sencillo de prueba para logging");
    logger.debug("Fin de procesamiento de petición");
  }
}

Ejemplos básicos de servlets

Servlet que genera texto plano

El siguiente ejemplo de servlet muestra una página con un mensaje de saludo: "Este es un servlet de prueba". Lo cargamos mediante petición GET.

package ejemplos;

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

@WebServlet(name="ejemplo1_1", urlPatterns="/ejemploservlet")
public class ClaseServlet extends HttpServlet {

  public void doGet(HttpServletRequest request,
                    HttpServletResponse response)
                 throws ServletException, IOException {
    PrintWriter out = response.getWriter();
    out.println ("Este es un servlet de prueba");
  }
}

Se obtiene un Writer para poder enviar datos al usuario. Simplemente se le envía la cadena que se mostrará en la página generada.

Servlet que genera una página HTML

Este otro ejemplo escribe código HTML para mostrar una página web.

package ejemplos;

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

@WebServlet(name="ejemplo1_2", urlPatterns="/ejemploservletHTML")
public class ClaseServletHTML extends HttpServlet {

  public void doGet(HttpServletRequest request,
                    HttpServletResponse response)
                 throws ServletException, IOException {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println ("<!DOCTYPE HTML PUBLIC \""+
                 "-//W3C//DTD HTML 4.0 " +
                 "Transitional//EN\">");
    out.println ("<HTML>");
    out.println ("<BODY>");
    out.println ("<h1>Titulo</h1>");
    out.println ("<br>Servlet que genera HTML");
    out.println ("</BODY>");
    out.println ("</HTML>");
  }
}

Para generar una página HTML con un servlet debemos seguir dos pasos:

  • Indicar que el contenido que se va a enviar es HTML (mediante el método setContentType() de HttpServletResponse):
response.setContentType("text/html");

Esta línea es una cabecera de respuesta, que veremos más adelante cómo utilizar. Hay que ponerla antes de obtener el Writer.

  • Escribir en el flujo de salida el texto necesario para generar la página HTML. La línea que genera el DOCTYPE no es necesaria, aunque sí muy recomendada para que se sepa qué versión de HTML se está empleando.
Servlet que utiliza parámetros de inicialización

Este otro ejemplo utiliza dos parámetros de inicialización externos:

package ejemplos;

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

@WebServlet(name="ejemplo1_3", urlPatterns="/ejemploservletInit")
public class ClaseServletInit extends HttpServlet {

  // Mensaje que se va a mostrar en la pagina
  String mensaje = "";
  // Numero de veces que se va a repetir el mensaje
  int contador = 1;

  // Metodo de inicializacion

  public void init(ServletConfig conf)
                           throws ServletException {
    super.init(conf);    // MUY IMPORTANTE

    mensaje = conf.getInitParameter("mensaje");
    if (mensaje == null)
      mensaje = "Hola";

    try
    {
      contador = Integer.parseInt(
         conf.getInitParameter("contador"));
    } catch (NumberFormatException e) {
      contador = 1;
    }
  }

  // Metodo para procesar una peticion GET

  public void doGet(HttpServletRequest request,
                    HttpServletResponse response)
               throws ServletException, IOException {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println ("<!DOCTYPE HTML PUBLIC \""+
                 "-//W3C//DTD HTML 4.0 " +
                 "Transitional//EN\">");
    out.println ("<HTML>");
    out.println ("<BODY>");

    for (int i = 0; i < contador; i++)
    {
      out.println (mensaje);
      out.println ("<BR>");
    }

    out.println ("</BODY>");
    out.println ("</HTML>");
  }
}
  • Se utiliza el método init() con un parámetro ServletConfig para poder tomar los parámetros externos. Es importante la llamada a super al principio del método.

  • Mediante el método getInitParameter() de ServletConfig obtenemos dos parámetros: mensaje y contador, que asignamos a las variables del mismo nombre. El primero indica el mensaje que se va a mostrar en la página, y el segundo el número de veces que se va a mostrar.

  • En doGet() hacemos uso de esos parámetros obtenidos, para mostrar el mensaje las veces indicadas.

  • Necesitaremos definir en el fichero web.xml los parámetros de inicio que el servlet va a leer.

1.4.3. JSP

JSP (JavaServer Pages) es una tecnología que permite incluir código Java en páginas web. El denominado contenedor JSP (que sería un componente del servidor web) es el encargado de tomar la página, sustituir el código Java que contiene por el resultado de su ejecución, y enviarla al cliente. Así, se pueden diseñar fácilmente páginas con partes fijas y partes variables. El siguiente es un ejemplo muy sencillo de página JSP:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Mi primera página JSP
</head>
<body>
<h1> Hoy es:
<%= new java.util.Date() %>
</h1>
</body>
</html>

Para ejecutar la página basta con colocarla en una aplicación web (por ejemplo, en WildFly, dentro de webapps/ROOT). No es necesario que sea en un directorio específico como ocurre con los servlets, sino que puede ir en cualquier directorio en el que se colocaría normalmente un HTML.

Aunque JSP y servlets parecen a primera vista tecnologías distintas, en realidad el servidor web traduce internamente el JSP a un servlet, lo compila y finalmente lo ejecuta cada vez que el cliente solicita la página JSP. Por ello, en principio, JSPs y servlets ofrecen la misma funcionalidad, aunque sus características los hacen apropiados para distinto tipo de tareas. Los JSP son mejores para generar páginas con gran parte de contenido estático. Un servlet que realice la misma función debe incluir gran cantidad de sentencias del tipo out.println() para producir el HTML. Por el contrario, los servlets son mejores en tareas que generen poca salida, datos binarios o páginas con gran parte de contenido variable. En proyectos más complejos, lo recomendable es combinar ambas tecnologías: los servlets para el procesamiento de información y los JSP para presentar los datos al cliente.

Traducción de los JSP a servlets

Como se ha comentado, la primera vez que se solicita una página JSP, el servidor genera el servlet equivalente, lo compila y lo ejecuta. Para las siguientes solicitudes, solo es necesario ejecutar el código compilado. El servlet generado de manera automática tiene un método _jspService que es el equivalente al service de los servlets "generados manualmente". En este método es donde se genera el código HTML, mediante instrucciones println y donde se ejecuta el código Java insertado en la página. Por ejemplo, la página primera.jsp podría generar un servlet con estructura similar al siguiente:

public void _jspService(HttpServletRequest request,
                        HttpServletResponse  response)
                        throws java.io.IOException, ServletException {
  JspWriter out = null;
  response.setContentType("text/html;ISO-8859-1");
  out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0
                Transitional//EN\">");
  out.println("<html>");
  out.println("<head>");
  out.println("<title>Mi primera pagina JSP");
  out.println("</head>");
  out.println("<body>");
  out.print("Hoy es ");
  out.println(new java.util.Date());
  out.println("</body>");
  out.println("</html>");
}

El directorio donde se coloca el servlet generado, así como su nombre, dependen del servidor de aplicaciones.

Inserción de código en páginas JSP

Hay tres formas de insertar código Java en una página JSP:

  • Expresiones de la forma <%= expresión %>: en este caso, la expresión se evalúa, su resultado se convierte a String y se inserta en la salida.

  • Scriptlets de la forma <% código %> : el código se ejecuta dentro del método _jspService del servlet generado.

  • Declaraciones de la forma <%! código %>: se insertan en el cuerpo del servlet generado, fuera de sus métodos.

Expresiones

Como se ha visto, se evalúan, su resultado se convierte a un String y se escriben en la salida (el objeto predefinido out). La forma de traducir una expresión a código de servlet es imprimiéndola en out (mediante una sentencia out.write(expresion)) o similar.

Scriptlets

Permiten ejecutar código arbitrario, cuyo resultado no es necesario enviar a la salida. Si desde un scriptlet se desea escribir algo en ésta, bastará con utilizar el objeto predefinido out. Un uso común de los scriptlets es hacer que ciertas partes de código HTML aparezcan o no en función de una condición. Por ejemplo:

<%
  java.util.Calendar ahora = java.util.Calendar.getInstance();
  int hora = ahora.get(java.util.Calendar.HOUR_OF_DAY);
%>
<b> Hola mundo, <i>
<% if ((hora>20)||(hora<6)) { %>
     buenas noches
<% }
   else if ((hora>=6)&&(hora<=12)) { %>
          buenos días
<%      }
    else { %>
          buenas tardes
<%      } %>
</i> </b>
Declaraciones

Permiten definir variables o métodos que se insertarán dentro del cuerpo del servlet generado. Esto da la posibilidad de sobreescribir los métodos jspInit y jspDestroy que son el equivalente en JSP del init y destroy de los servlets. Las variables declaradas conservarán su valor entre sucesivas llamadas a la página, ya que son variables miembro del servlet y no locales al método jspService. Esto nos permite, por ejemplo, crear un contador de accesos a la página:

<%! private int accesos = 0; %>
<h1> Visitas: <%= ++accesos %> </h1>

1.5. Ejercicios

Antes de empezar a crear los proyectos, debes descargarte el repositorio git java_ua/ejercicios-cweb en el que vas a crear los proyectos del módulo. El repositorio remoto contiene las plantillas que vamos a usar a lo largo del módulo.

En primer lugar deberemos acceder a dicho repositorio en bitbucket y realizar un Fork en nuestra cuenta personal.

Realizar un Fork en bitbucket

De esta forma tendremos una copia propia con permisos de escritura. Una vez contamos con nuestra copia podremos realizar un Clone en nuestra máquina. En primer lugar consultamos en bitbucket el comando necesario para hacer el Clone:

Dirección para clonar el repositorio de bitbucket

Podemos copiar el comando y pegarlo en un terminal para clonar el repositorio en nuestra máquina:

git clone https://bitbucket.org/<alumno>/ejercicios-cweb

De esta forma se crea en nuestro ordenador el directorio ejercicios-cweb y se descarga en él una plantilla inicial de los proyectos del módulo y un fichero .gitignore. A partir de este momento se puede trabajar con dichos proyectos y realizar Commit y Push cuando sea oportuno:

cd ejercicios-cweb
git add .
git commit -a -m "Mensaje de commit"
git push origin master

1.5.1. Aplicación web en WildFly (0.5 puntos)

Vamos a probar el servidor de aplicaciones WildFly y a subir recursos estáticos a él para comprobar que funciona.

a) Comenzaremos poniendo en marcha el servidor entrando en el directorio $WILDFLY_HOME/bin y ejecutando el comando:

./standalone.sh

Comprobar que WildFly ha arrancado accediendo a http://localhost:8080/ desde cualquier navegador. Debería aparecer la página principal del servidor.

b) Vamos a crear nuestra propia aplicación web y desplegarla en WildFly. En el directorio cweb-comentarios de las plantillas de la sesión hay una sencilla aplicación web que permite mantener una página y que los visitantes puedan añadir comentarios. Vamos a empaquetar y desplegar la aplicación en un fichero war y desplegarla.

  • Empaquetamiento en .war. Observad la estructura de directorios que cuelga del directorio comentarios. Podemos empaquetar la aplicación mediante la herramienta jar incluída en el JDK, como se especifica en los apuntes:

      jar cMvf cweb-comentarios.war *
    

    La operación anterior hay que hacerla desde dentro del directoriocweb-comentarios, es decir, el .war creado no debe contener la carpeta "cweb-comentarios" propiamente dicha, sino lo que contiene ésta.

  • Despliegue manual en WildFly. Para desplegar el .war manualmente basta con dejarlo en la carpeta $WILDFLY_HOME/standalone/deployments. Observad que transcurridos unos segundos el .war se desplegará de forma automática. Para probar la aplicación abre un navegador y accede a la URL:

      http://localhost:8080/cweb-comentarios/comentarios.jsp
    

1.5.2. Hola mundo con IntelliJ (0.5 puntos)

Vamos a crear y desplegar una aplicación Hola mundo mediante IntelliJ.

a) Configuramos el servidor WildFly en IntelliJ.

  • Crear una instancia del servidor WildFly: Con la opción File > Settings … > Application Servers crearemos una nueva instancia de WildFly dentro de IntelliJ. En el cuadro deplegable para añadir un servidor simplemente tenemos que asegurarnos de elegir JBoss 8.1.0 Final > Local, e indicar la ruta donde está instalado (/usr/local/wildfly).

*b)*Crearemos un proyecto Maven de tipo webapp-javaee7 desde IntelliJ.

  • Creamos el proyecto con el arquetipo webapp-javaee7 (deberemos añadir el arquetipo a la lista si es la primera vez que lo utilizamos).

  • Como GroupId utilizaremos org.expertojava.cweb.

  • Como ArtifactId introduciremos: cweb-hola. Utilizaremos este mismo nombre posteriormente como nombre del proyecto IntelliJ.

  • Dejamos la versión como 1.0-SNAPSHOT.

*c)*Desplegamos el proyecto que acabamos de crear en WildFly.

  • Introducimos el elemento finalName en el fichero pom.xml para que la aplicación se despliegue en un contexto cweb-hola.

  • Creamos un perfil de ejecución que utilice el servidor configurado en el primer paso (Run > Edit Configurations …​).

  • Añadimos al perfil de ejecución el artefacto de tipo war que tenemos creado por defecto (pestaña Deployments).

  • Lanzamos el despliegue. Veremos la página de prueba que ha creado como plantilla.

*d)*Añadimos desde IntelliJ un fichero web.xml al proyecto.

  • Entramos en File > Project Structure …​ > Facets.

  • Seleccionamos el facetWeb y añadimos un descriptor de despliegue.

*e)*Crear un nuevo servlet dentro del proyecto, en un paquete org.expertojava.cweb.hola, con nombre HolaMundoServlet. Mapearemos el servlet a la dirección /HolaMundoServlet utilizando anotaciones.

*f)*Introducir en el método doGet del servlet el código para que muestre como salida el texto "Hola Mundo":

PrintWriter out = response.getWriter();
out.println ("Hola Mundo");

*g)*Ejecutar la aplicación web en WildFly y comprobar que el servlet funciona correctamente, accediendo a la dirección a la que está mapeado.

h) Vamos a hacer que el servlet que hemos añadido se muestre de forma automática como página principal de nuestra aplicación web. En el web.xml hay una sección llamada welcome-file-list que lista las páginas que el servidor debe buscar cuando se llame a la aplicación sin especificar la página a mostrar. Indicar únicamente /HolaMundoServlet en esta lista y comprobar que funciona adecuadamente, es decir que al llamar a http://localhost:8080/cweb-hola aparece dicho servlet.

1.5.3. Servlet que muestra la fecha y hora actuales (0.5 puntos)

Completar el servlet org.expertojava.cweb.ejercicios.FechaServlet de la aplicación cweb-servlets para que, tanto por GET como por POST, muestre una página HTML con la fecha y hora actuales en una cabecera <h3>, y en el <title> de la página. Para ello podéis utilizar la clase java.util.Date, y sacar por la salida del servlet la hora en formato cadena:

public void doGet(...) throws ...
{
  String fecha = "" + new java.util.Date();
  response.setContentType(...);
  out = response.getWriter();
  ... // sacar la fecha tanto en el TITLE como en una cabecera H3
}

Una vez hecho, configurad el descriptor de la aplicación para que el servlet se mapee a la dirección /FechaHora.

1.5.4. Servlet que muestra parámetros de inicio (0.5 puntos)

Crear un servlet org.expertojava.cweb.ejercicios.ParamIniServlet en la aplicación cweb-servlets que muestre en una tabla el nombre y el valor de todos los parámetros de inicio que se tengan configurados para ese servlet en el fichero descriptor (web.xml). La tabla tendrá dos columnas: una con el nombre del parámetro y otra con el valor.

Una vez hecho, probadlo añadiéndole en el fichero web.xml 3 parámetros de inicio con nombres param1, param2 y param3 y valores val1, val2 y val3. Para ello deberéis dar un nombre al servlet (el nombre es arbitrario).

public void doGet(...) throws...
{
    Enumeration<String> nombres = this.getInitParameterNames();
    while (nombres.hasMoreElements()) {
        String nombre = nombres.nextElement();
        String valor = this.getInitParameter(nombre);
        ... // Mostrar nombre y valor en una tabla
    }
}

1.5.5. Configurar logging en servlets (0.5 puntos)

En la aplicación cweb-servlets tenemos dos servlets, ServletLog4J1 y ServletLog4J2 en el paquete org.expertojava.cweb.ejercicios. Queremos configurar las librerías de logging para poder ver los mensajes que emiten. Se pide:

  • Comprobar que las librerías de logging de commons-logging y log4j están correctamente configuradas como dependencias en el pom.xml.

  • Comprobar que los ficheros de configuración commons-logging.properties y log4j.properties están en la carpeta de fuentes llamada resources. Estos ficheros están configurados para que volcar los mensajes de ambos servlets (de tipo INFO o superior) a un fichero /home/expertojava/errores.log, con el formato:

dd/MM/aaaa hh:mm:ss - prioridad - texto del mensaje - salto de línea

  • El servlet ServletLog4J2 no saca mensajes de log. Añadid las líneas de código necesarias para que saque un mensaje de tipo INFO cuando empiece a procesar la petición (al inicio del doGet) y otro cuando la termine, anotando el tiempo transcurrido entre ambos mensajes (puede serte de utilidad el método System.currentTimeMillis() de Java).

Probad a llamar a los servlets ServletLog4J1 o ServletLog4J2 alguna vez, y que generen logs en el fichero errores.log que viene por defecto en el fichero de configuración.