====== 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. @startuml participant "Conector" as C participant "Pool" as P participant "hilo exec-N" as T participant "Filtros y servlet" as F C -> P : acepta la conexión P -> T : asigna la petición T -> F : ejecuta la cadena F --> T : respuesta T -> P : hilo libre para otra petición @enduml * El nombre del hilo lo delata en los logs: ''http-nio-8080-exec-3'' es el worker 3 del pool. * Una petición en curso es un hilo ocupado; al responder, el hilo vuelve al pool y sirve a otro cliente. * El pool tiene techo: ''maxThreads'' marca el máximo de peticiones simultáneas; con más carga, las que no caben esperan en cola. ''minSpareThreads'' deja hilos creados de antemano. * La configuración va en ''conf/server.xml'' (manera tradicional) o en las propiedades del embebido. 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 } } * ''contextInitialized'': se ejecuta una vez al desplegar la aplicación; es el sitio para preparar lo que servirá a servlets y filtros: abrir el pool de conexiones a la base de datos, leer la configuración... * ''contextDestroyed'': se ejecuta al parar la aplicación; libera esos recursos. * Funciona igual en los dos modos: el contenedor registra el listener en el arranque, sea Tomcat instalado aparte o embebido. 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 └── ... * El contenido fuera de ''WEB-INF'' se sirve como **archivos estáticos** (HTML, CSS, JS). * ''WEB-INF'' no es accesible por HTTP: contiene código y configuración. * La aplicación se empaqueta como ''.war'' (*Web ARchive*) para desplegarla. ===== 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: bookServlet com.example.BookServlet bookServlet /books 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 └── ... * Se copia el ''.war'' en ''webapps'' y Tomcat lo despliega en su **context path**: ''miapp.war'' queda publicado en ''http://localhost:8080/miapp''. * El servidor se arranca y se para con los scripts de ''bin''; los logs quedan en ''logs''. * Un mismo Tomcat puede servir varias webapps, con un pool de hilos compartido. 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''.