Configurando Servlets y JSP en Jetty

Jetty es un servidor web ligero y flexible que se utiliza ampliamente en aplicaciones Java. En esta entrada, veremos cómo configurar un proyecto básico que utiliza servlets y JSP en Jetty, ideal para aprender los fundamentos antes de pasar a frameworks más avanzados como Spring Boot.

Requisitos previos

  1. Tener conocimientos básicos de Java y familiaridad con conceptos web como HTTP.
  2. Contar con Maven para gestionar dependencias.
  3. Tener instalado un JDK (Java Development Kit) compatible, como JDK 8 o superior.
  4. Cualquier ide para trabajar con Java (Yo estoy familiarizado con Netbeans, pero puedes elegir el que deseas.)

Paso 1: Crear el Proyecto Maven

Estructura del Proyecto

mi-proyecto/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── ejemplo/
│   │   │           ├── servlet/
│   │   │           │   ├── HolaMundoServlet.java
│   │   │           │   └── IndexServlet.java
│   │   │           └── AppMain.java
│   └── webapp/
│       ├── WEB-INF/
│       │   ├── web.xml
│       │   └── jsp/
│       │       └── index.jsp
└── pom.xml

Se debe crear un proyecto Maven de tipo aplicación de consola o aplicación Java, que integre Jetty embebido en el proyecto. Además, debe incluir las dependencias necesarias para soportar Servlets y JSP, y estar empaquetado como un JAR ejecutable.

Haz clic en la imagen para verla en tamaño completo.

Haz clic en la imagen para verla en tamaño completo.

Paso 2: Editar el archivo pom.xml

En Maven, necesitamos definir las dependencias y plugins necesarios para Jetty. Aquí tienes un ejemplo básico:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.ejemplo</groupId>
    <artifactId>servlet</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.release>17</maven.compiler.release>
        <exec.mainClass>com.ejemplo.AppMain</exec.mainClass>
    </properties>
   <!-- DEPENDENCIA DE JSON (OPCIONAL) -->    
     <dependencies>
        <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.11.0</version> <!-- Reemplaza con la última versión disponible -->
    </dependency>
      
        
        <!-- DEPENDENCIAS NECESARIAS DE JETTY PARA CORRER JSP y servlets -->
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-server</artifactId>
        <version>11.0.23</version>
    </dependency>


    <dependency>
        <groupId>jakarta.servlet</groupId>
        <artifactId>jakarta.servlet-api</artifactId>
        <version>5.0.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-webapp</artifactId>
        <version>11.0.23</version>
    </dependency>

    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>apache-jsp</artifactId>
        <version>11.0.23</version>
    </dependency>
    <!-- JSTL (Jakarta Standard Tag Library) -->
    <dependency>
        <groupId>jakarta.servlet.jsp.jstl</groupId>
        <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
        <version>2.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.web</groupId>
        <artifactId>jakarta.servlet.jsp.jstl</artifactId>
        <version>2.0.0</version>
    </dependency>
    <!-- Additional Jetty Dependencies -->
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-annotations</artifactId>
        <version>11.0.23</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-plus</artifactId>
        <version>11.0.23</version>
    </dependency>
    <!-- DEPENDENCIA DE MYSQL (OPCIONAL) -->    
<dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version> 
</dependency>        
</dependencies>

<build>
        <plugins>
            <!-- Plugin para compilar con Java 21 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.13.0</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>

            <!-- Plugin para ejecutar la aplicación desde Maven -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <mainClass>com.ejemplo.AppMain</mainClass>
                </configuration>
            </plugin>
            
            <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.7.1</version> <!-- Usa la versión más reciente disponible -->
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>com.ejemplo.AppMain</mainClass> <!-- Cambia al nombre de tu clase principal -->
                    </manifest>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        </plugins>
    </build>
</project>
    

Dependencias Opcionales en el pom.xml

En el archivo pom.xml de este proyecto, se han añadido las siguientes dependencias como opcionales, ya que su uso depende de las necesidades específicas de cada implementación:


<dependencies> <!-- DEPENDENCIA JSON (OPCIONAL) --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.9</version> <optional>true</optional> </dependency> <!-- DEPENDENCIA DE MYSQL (OPCIONAL) --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.34</version> <optional>true</optional> </dependency> </dependencies>

Estas dependencias no son estrictamente necesarias para el funcionamiento básico del servlet. Su inclusión se basa en los requisitos específicos del proyecto, como trabajar con JSON o conectarse a una base de datos MySQL. Si no las necesitas, puedes eliminarlas sin afectar la configuración del servlet.

Paso 3: Crear un Servlet.

Ahora crearemos un servlet llamado HolaMundoServlet, que es una clase Java encargada de procesar solicitudes HTTP y devolver una respuesta. En este caso, el servlet responderá con un mensaje 'Hola Mundo' en formato HTML.


package com.ejemplo.servlet;

import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HolaMundoServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // Establecer el tipo de contenido como HTML
        response.setContentType("text/html");

        // Obtener el flujo de salida para escribir la respuesta
        PrintWriter out = response.getWriter();
 out.println("<html>");
        out.println("<head><title>Hola Mundo Servlet</title></head>");
        out.println("<body>");
        out.println("<h1>¡Hola Mundo desde el Servlet!</h1>");
        out.println("</body>");
        out.println("</html>");
        // Imprimir un mensaje en HTML
       
    }
}

Explicación:

  1. Extiende HttpServlet: La clase HolaMundoServlet extiende de HttpServlet, que es la clase base para crear servlets en Java.
  2. Método doGet: El método doGet se utiliza para manejar solicitudes HTTP GET. Aquí es donde se define la lógica para responder a la solicitud.
  3. PrintWriter para la salida: Usamos el objeto PrintWriter para enviar contenido al navegador. En este caso, estamos enviando una respuesta HTML simple con un encabezado que dice "¡Hola Mundo desde el Servlet!".
  4. Establecer el tipo de contenido: response.setContentType("text/html") asegura que la respuesta sea interpretada como HTML por el navegador.

Paso 4: Crear el IndexServlet

En esta parte, crearemos el IndexServlet, que será el encargado de mostrar la página principal de la aplicación. Este servlet se encargará de responder a las solicitudes en la raíz de la aplicación (/) y redirigir al usuario al archivo JSP correspondiente, en este caso, index.jsp.

El código para el IndexServlet es el siguiente:

package com.ejemplo.servlet;

import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

public class IndexServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // Redirigir al archivo JSP (index.jsp)
        request.getRequestDispatcher("/WEB-INF/jsp/index.jsp").forward(request, response);
    }
}

Explicación del código:

  • doGet: Este método maneja las solicitudes GET. Cuando el usuario accede a la URL raíz de la aplicación, el servlet captura la solicitud y la redirige a la página JSP.
  • request.getRequestDispatcher("/WEB-INF/jsp/index.jsp").forward(request, response);: Esta línea le indica al servlet que, en lugar de devolver contenido HTML directamente, debe reenviar la solicitud al archivo JSP index.jsp dentro de la carpeta WEB-INF/jsp. La ventaja de usar esta ruta es que los archivos JSP en la carpeta WEB-INF no son accesibles directamente desde la URL, lo que mejora la seguridad.

Paso 5: Crear el archivo index.jsp

El archivo index.jsp es el archivo que se mostrará al usuario cuando acceda a la aplicación a través de la URL raíz (/). Este archivo es procesado por el servlet IndexServlet, que redirige la solicitud hacia él.

Aquí tienes un ejemplo básico de contenido para el archivo index.jsp:

<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Bienvenido a la Aplicación Jetty</title>
</head>
<body>
    <h1>¡Bienvenido a la aplicación con Jetty!</h1>
    <p>Esta es la página principal de la aplicación. Estás viendo un archivo JSP que fue procesado por un servlet.</p>
    <p>Para más información, consulta la documentación o explora los otros recursos de la aplicación.</p>
</body>
</html>

Creación manual del archivo web.xml en Jetty embebido

A diferencia de otros contenedores de servlets como Tomcat, GlassFish o Payara, Jetty no genera automáticamente el archivo web.xml cuando configuramos un proyecto embebido. Por ello, es necesario crearlo manualmente para definir los servlets, mapeos y otras configuraciones.

El archivo web.xml es el descriptor de despliegue de la aplicación web, donde se configuran servlets, filtros y otros componentes clave de la aplicación. 

A continuación, te mmuestro cómo hacerlo:

1. Ubicación del archivo web.xml

El archivo web.xml debe colocarse en la siguiente ruta dentro de tu proyecto Maven:

src/main/webapp/WEB-INF/web.xml


2. Contenido básico del archivo web.xml

Agrega la siguiente configuración básica al archivo:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    
    
    <servlet>
        <servlet-name>HolaMundoServlet</servlet-name>
        <servlet-class>com.ejemplo.servlet.HolaMundoServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>HolaMundoServlet</servlet-name>
        <url-pattern>/holaMundo</url-pattern>
    </servlet-mapping>
    
    <servlet>
        <servlet-name>IndexServlet</servlet-name>
        <servlet-class>com.ejemplo.servlet.IndexServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>IndexServlet</servlet-name>
        <url-pattern>/index</url-pattern>
    </servlet-mapping>
    
    <servlet-mapping>
        <servlet-name>IndexServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    
<!-- CON ESTA CONFIGURACION INDICO EL RUTEO DE LOS RECURSOS ESTATICOS Y SUS RESPECTIVAS CARPETAS SEAN SCRIPTS HTML Y CSS -->    
<servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/css/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/scripts/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/images/*</url-pattern>
    </servlet-mapping>
   <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/messageview/*</url-pattern>
    </servlet-mapping>
</web-app>


Explicación del Código:

  • Definición del Servlet: En <servlet>, se especifican el nombre del servlet (HolaMundoServlet) y la clase Java que lo implementa (com.ejemplo.servlet.HolaMundoServlet).
  • Mapeo del Servlet: En <servlet-mapping>, se asocia el servlet con una URL específica (/holaMundo). Esto permite que el servlet responda cuando se accede a esa ruta.


Opción con Anotaciones (Servlet 4.0 o superior)

Si tu servidor soporta Servlet 4.0 o superior, puedes simplificar la configuración usando anotaciones. Esto elimina la necesidad de modificar el archivo web.xml

import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/holaMundo")
public class HolaMundoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.getWriter().write("¡Hola, Mundo!");
    }
}

 

Con la anotación @WebServlet("/holaMundo"), el servlet se registra automáticamente y estará accesible en la ruta /holaMundo.

Nota: Aunque este método es más simple, es posible que se requieran configuraciones adicionales dependiendo de tu entorno o del servidor de aplicaciones que estés utilizando.

¿Cuál Opción Usar?

  • Sin Anotaciones (Uso de web.xml): Ideal para aplicaciones más tradicionales o cuando se necesita un control explícito.
  • Con Anotaciones: Recomendado para proyectos modernos, siempre que el servidor soporte Servlet 4.0 o superior.

Consideraciones Finales

Si eliges usar anotaciones, verifica la compatibilidad de tu servidor y considera que algunas configuraciones, como la gestión de dependencias o la inicialización de recursos, podrían necesitar ajustes adicionales. Si enfrentas problemas, siempre puedes optar por el método basado en web.xml.

Clase principal para iniciar Jetty: AppMain

La siguiente clase define la configuración principal para iniciar un servidor Jetty embebido en el puerto 9090 y servir JSPs y Servlets desde el proyecto Maven. Este ejemplo es completamente funcional y puede ser adaptado a tus necesidades.

Código de la clase AppMain

package com.ejemplo;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;

/**
 * Clase principal para configurar y levantar el servidor Jetty.
 */
public class AppMain {

    public static void main(String[] args) throws Exception {
        // Crear una instancia del servidor en el puerto 9090
        Server server = new Server(9090);

        // Configurar el contexto web para JSP y Servlets
        WebAppContext context = new WebAppContext();
        context.setContextPath("/"); // Define el path base de la aplicación
        context.setResourceBase("src/main/webapp"); // Carpeta que contiene los JSP y recursos estáticos
        context.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false"); // Deshabilita el listado de directorios
        context.setParentLoaderPriority(true); // Prioriza el ClassLoader del servidor

        // Asignar el contexto al servidor
        server.setHandler(context);

        // Iniciar el servidor
        server.start();
        server.join();
    }
}

Arranque del Proyecto con Jetty

  1. Generación y ejecución desde el IDE:
    Una vez con el proyecto construido y configurado con Jetty en el archivo pom.xml, compílalo y ejecútalo directamente desde tu IDE. Esto iniciará el servidor Jetty embebido.

  2. Acceso al proyecto desplegado:
    Una vez que el servidor Jetty esté en funcionamiento, podrás acceder a la aplicación desde la siguiente URL:

http://localhost:9090/

El navegador mostrará el siguiente resultado


Probar el Servlet "Hola Mundo"

Accede al servlet desde el navegador:
Una vez que el servidor esté en ejecución, abre tu navegador y visita la siguiente URL para probar el servlet HolaMundoServlet:

http://localhost:9090/holaMundo

El resultado será:Para finalizar 

Espero que esta guía te haya sido útil para configurar y poner en marcha un proyecto con Jetty, gestionar servlets y probar su funcionamiento. Ten en cuenta que, al utilizar JSP, pueden surgir limitaciones en la depuración debido a la integración con el servidor embebido.

Si deseas explorar más a fondo el proyecto o probarlo directamente, he dejado el código fuente disponible para su descarga. Aquí

No dudes en dejar un comentario si tienes alguna duda o sugerencia. ¡Feliz desarrollo y éxito con tus proyectos!

Comentarios

Entradas populares de este blog

Firma de un Documento XML con Certificado Digital en Java para Uso Tributario en Chile

RESOLUCION SET BASICO DE FACTURA ELECTRÓNICA SII