Tabla de Contenidos

06 - Tomcat: tradicional y embebido

Tomcat es un contenedor de servlets: gestiona el ciclo de vida de los servlets y filtros, atiende las conexiones y reparte cada petición en un hilo de su pool. Es la versión “de verdad” del servidor que se construyó en los temas anteriores, y se usa de dos maneras: instalado aparte (la manera tradicional) o embebido dentro de la aplicación (la habitual hoy).

1. Qué hace

Capa Cómo la resuelve Tomcat
Sockets y protocolo HTTP Conector HTTP en un puerto (por defecto 8080)
Concurrencia Pool de hilos worker (http-nio-8080-exec-N, tema 05)
Enrutado Cada ruta (@WebServlet, @WebFilter) a su componente
Ciclo de vida init/destroy de servlets y filtros; listeners al arrancar y parar la webapp
Recursos Archivos estáticos, errores HTTP, sesiones (no se usan aquí)

Tomcat ejecuta exactamente el modelo de los temas anteriores: acepta conexiones, las reparte en hilos del pool y delega cada petición en un servlet a través de la cadena de filtros. Lo que cambia según el modo de uso es quién instala y arranca ese servidor.

2. Hilos de Tomcat

El conector de Tomcat es el pool del tema 05 hecho servidor: acepta las conexiones y reparte cada petición en un hilo worker libre del pool.

ConectorPoolhilo exec-NFiltros y servletConectorPoolhilo exec-NFiltros y servletConectorPoolhilo exec-NFiltros y servletacepta la conexiónasigna la peticiónejecuta la cadenarespuestahilo libre para otra petición

Las reglas de estado compartido del tema 05 aplican tal cual: servlets y filtros atienden muchas peticiones con una sola instancia.

Los workers del pool se reutilizan: lo que un hilo guarda en ThreadLocal para una petición sigue ahí cuando ese mismo hilo atiende la siguiente, si nadie lo limpia. Con pool de hilos, limpiar el ThreadLocal forma parte de terminar la petición.

3. Listeners

Los listeners son el tercer tipo de componente del contenedor, junto a servlets y filtros: en lugar de atender peticiones, añaden código al ciclo de vida de la webapp:

@WebListener
public class AppListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent event) {
        // al desplegar la webapp: preparar recursos compartidos
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        // al pararla: liberarlos
    }
}

Con servlets, filtros (tema 04) y listeners, está completo el cuadro de componentes que un contenedor gestiona.

4. Estructura de una webapp

La unidad de despliegue es la webapp, una carpeta con estructura fija:

miapp/
├── WEB-INF/
│   ├── web.xml            (configuración, opcional con anotaciones)
│   ├── classes/           (clases compiladas)
│   └── lib/               (JARs de dependencias)
├── index.html
└── ...

5. Anotaciones frente a web.xml

Las rutas se declaran con anotaciones:

@WebServlet("/books")
public class BookServlet extends HttpServlet { /* ... */ }

@WebFilter("/*")
public class TokenFilter implements Filter { /* ... */ }

O en web.xml con el mismo resultado:

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" version="6.0">
  <servlet>
    <servlet-name>bookServlet</servlet-name>
    <servlet-class>com.example.BookServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>bookServlet</servlet-name>
    <url-pattern>/books</url-pattern>
  </servlet-mapping>
</web-app>

La combinación habitual: anotaciones para lo simple, web.xml cuando la configuración cambia por entorno.

6. Manera tradicional: servidor independiente

En el modo tradicional, Tomcat se instala aparte y las aplicaciones se despliegan en él:

apache-tomcat/
├── bin/        scripts de arranque y parada (startup.sh, shutdown.sh)
├── conf/       configuración; server.xml fija el puerto (por defecto 8080)
├── logs/       registro del servidor (catalina.out)
├── webapps/    aplicaciones desplegadas
└── ...

Es el modo clásico de producción —y el que sigue usándose cuando varias aplicaciones comparten servidor—, pero obliga a instalar y versionar el servidor por separado de la aplicación.

7. Tomcat embebido

En el modo embebido, la aplicación incluye el servidor como una dependencia más y lo arranca desde su propio main:

public class App {
    public static void main(String[] args) throws Exception {
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);

        Context context = tomcat.addContext("", System.getProperty("java.io.tmpdir"));
        Tomcat.addServlet(context, "books", new BookServlet());
        context.addServletMappingDecoded("/books", "books");

        tomcat.start();               // conector, pool y ciclo de vida
        tomcat.getServer().await();   // el main queda a la espera
    }
}

No hay webapps ni .war: el jar de la aplicación es el servidor. Arrancar la aplicación es arrancar Tomcat.

Así funciona Spring Boot: la aplicación declara el arranque y el contenedor viene dentro. En 2.º DAW se usará de esta forma; el main de un proyecto Spring Boot levanta un Tomcat embebido idéntico al de este tema.

8. Tradicional frente a embebido

Tradicional Embebido
Instalación Servidor compartido, se instala aparte El servidor viene dentro de la app
Despliegue Copiar el .war en webapps Ejecutar el jar de la aplicación
Configuración conf/server.xml del servidor Propiedades de la aplicación
Escenarios Varias apps en un mismo servidor Una app, un servidor (modelo dominante hoy)
En los dos modos el comportamiento es idéntico: conexiones aceptadas, pool de hilos y peticiones delegadas en servlets y filtros. Lo que cambia es la operativa: instalar y desplegar frente a ejecutar un único jar.