Integrar Thymeleaf a un Servlet Java

En el tutorial previo a este, vimos los Servlet, aprendimos a crearlos, instanciarlos y configurarlos, además escribimos el código necesario para responder a una petición HTTP GET, la respuesta a dicha petición la generamos usando la clase PrintWriter para escribir línea a línea el documento HTML de respuesta, esto suele ser un proceso tedioso, es ahí en donde entra Thymeleaf, esta tecnología nos permite generar un documento HTML dinámico a partir de una plantilla.

Thymeleaf es un moderno motor de plantillas que puede ser utilizado para procesar plantillas HTML, XML, JavaScript, CSS y texto plano, para esta ocasión nos enfocaremos en las plantillas HTML5, podemos agregar contenido dinámico usando la sintaxis de expresiones del lenguaje OGNL (Object-Graph Navigation Language).

Crear un proyecto Thymeleaf

Para crear nuestro proyecto demostrativo usaremos el IDE NetBeans 8.x, creamos un proyecto Maven Web del mismo modo que lo hicimos en el tutorial Java Servlet, de hecho el proyecto es el mismo salvo que debemos agregar la dependencia para Thymeleaf, el archivo pom.xml editado queda así:

<?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>tutor.programacion</groupId>
    <artifactId>ThymeleafServletTutorial</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>
    
    <dependencies>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf</artifactId>
            <version>3.0.2.RELEASE</version>
        </dependency>

    </dependencies>
    
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
      
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Para utiliza el motor de plantillas Thymeleaf en nuestro proyecto Java debemos hacer los siguiente:

  • Configurar el ServletContextTemplateResolver, este componente es el encargado de localizar las plantillas y configurar todo lo pertinente a las mismas.
  • Crear un TemplateEngine, este se encargara de procesar las plantillas y generar el resultado final, este componente hace uso del primero para ubicar las plantillas.

Para esto diseñamos la presente clase:

    public class PrimerThymeleaf {
    
        private final TemplateEngine templateEngine;
    
        public PrimerThymeleaf(final ServletContext ctx) {
    
            ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(ctx);
            templateResolver.setTemplateMode(TemplateMode.HTML);
            templateResolver.setPrefix("/WEB-INF/templates/");
            templateResolver.setSuffix(".html");
            templateResolver.setCacheTTLMs(3600000L);
            templateResolver.setCacheable(true);
    
            templateEngine = new TemplateEngine();
            templateEngine.setTemplateResolver(templateResolver);
        }
    
        public TemplateEngine getTemplateEngine() {
            return templateEngine;
        }
    }
    

    Cuando deseemos procesar una plantilla llamaremos al método process() de la clase TemplateEngine, debemos indicarle el nombre de la plantilla, por ejemplo: "home" este nombre se combina con el valor de las propiedades Prefix y el Suffix para obtener la ubicación de la plantilla, siguiendo el ejemplo: "/WEB-INF/templates/home.html".

    Plantilla HTML para Thymeleaf

    El método setCacheable(false) desactiva la caché, cuando estamos probando nuestra aplicación puede ser conveniente mantener esta opción desactivada, por defecto su valor es true.

    Procesar plantilla Thymeleaf

    Creamos el Servlet como en el tutorial previo, extendemos la clase HttpServlet y sobre escribimos el método doGet(), adicionalmente también sobre escribimos el método init() y lo aprovechamos para inicializar la clase que nos ayuda a utilizar Thymeleaf.

    @WebServlet("/")
    public class PrimerServlet extends HttpServlet {
    
        private PrimerThymeleaf primerThymeleaf;
        private ServletContext servletContext;
    
        @Override
        public void init(ServletConfig config) throws ServletException {
            servletContext = config.getServletContext();
            primerThymeleaf = new PrimerThymeleaf(servletContext);
        }
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            response.setContentType("text/html;charset=UTF-8");
            response.setHeader("Pragma", "no-cache");
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expires", 0);
                
            WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());
            ctx.setVariable("today", new Date());
            
            TemplateEngine engine = primerThymeleaf.getTemplateEngine();
            engine.process("home", ctx, response.getWriter());
        }
    }
    

    El método init(...) se ejecuta al iniciar el Servlet, obtenemos y guardamos una referencia al ServletContext, y además instanciamos la clase PrimerThymeleaf.

    public void init(ServletConfig config) throws ServletException {
        servletContext = config.getServletContext();
        primerThymeleaf = new PrimerThymeleaf(servletContext);
    }
    

    El método doGet(...) será invocado cada vez que recibamos una petición de tipo HTTP GET, lo primero que hacemos en configurar las cabeceras HTTP de la respuesta, de momento no hacemos nada especial, el código solo es para demostración.

    response.setContentType("text/html;charset=UTF-8");
    response.setHeader("Pragma", "no-cache");
    response.setHeader("Cache-Control", "no-cache");
    response.setDateHeader("Expires", 0);
    

    Para usar el motor de plantillas debemos crear un contexto, este objeto contiene toda la información necesaria para el procesamiento de las plantillas, por ejemplo, las variables, la localización, etc.

    En este ejemplo usamos el método setVariable(...) para agregar una variable llamada today que indica la fecha actual, usando este nombre podemos acceder a este valor desde la plantilla, más adelante lo veremos.

    WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());
    ctx.setVariable("today", new Date());
    

    Finalmente obtenemos el TemplateEngine y procesamos la plantilla indicada usando el método process(...) debemos pasarle el nombre de la plantilla, el contexto y el Writer usado para escribir la salida.

    TemplateEngine engine = primerThymeleaf.getTemplateEngine();
    engine.process("home", ctx, response.getWriter());
    

    Recordemos que response.getWriter() obtiene el objeto PrintWriter que nos permite escribir la salida.

    La plantilla HTML

    Nos falta por revisar la plantilla /home.html, la misma es la siguiente:

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
        <head>
            <title>Servlet Thymeleaf Tutorial</title>
            <meta charset="UTF-8">
        </head>
        <body>
            <p th:text="#{home.welcome}">Mensaje de bienvenida</p>
            <p>Hoy es: <span th:text="${today}">01 Enero 2017</span></p>
        </body>
    </html>
    

    La mayoría del documento es HTML salvo las partes que contienen la etiqueta th:text="..." la misma reemplaza el texto del cuerpo de la etiqueta que la contiene con el resultado de evaluar la expresión indicada, vemos las siguiente expresiones:

    #{...} obtiene el texto internacional identificado por la clave indicada, si lo deseamos podemos definir mensajes en distintos idiomas, para esto debemos crear un archivo para cada uno de ellos, por ejemplo:

    • /WEB-INF/templates/home_es.properties mensajes es español
    • /WEB-INF/templates/home_en.properties mensajes en inglés
    • /WEB-INF/templates/home_ru.properties mensajes es ruso

    Los mensajes en el idioma español se definen en el archivo /home_es.properties de la siguiente manera:

    home.welcome=Bienvenidos a la tecnologia Thymeleaf
    home.author=Tutor de Programacion 2017
    

    Los archivos para el inglés y ruso son similares a este, solo debemos traducir los mensajes, las claves permanecen iguales.

    Thymeleaf utiliza el Locale pasado atreves del contexto para seleccionar que mensajes debe utilizar.

    ${...} este expresión obtiene el valor de la variable indicada, recordemos que previamente usamos el método ctx.setVariable("today", new Date()) para agregar una variable, primero indicamos el nombre luego el valor, para obtener este valor desde la plantilla usamos: ${today}.

    Esto es lo que tenemos:

    <!DOCTYPE html>
    <html>
        <head>
            <title>Servlet Thymeleaf Tutorial</title>
            <meta charset="UTF-8">
        </head>
        <body>
            <p>Bienvenidos a la tecnologia Thymeleaf</p>
            <p>Hoy es: <span>Sun Jul 09 07:56:14 CST 2017</span></p>
        </body>
    </html>
    

    Tutorial Thymeleaf Java Servlet

    Terminamos de momento, hasta la próxima.

    Descargar proyecto: thymeleaf-servlet.zip

    Comentarios

    Entradas populares de este blog

    Conectar SQL Server con Java

    Entrenar OpenCV en Detección de Objetos

    Procesamiento de imágenes en OpenCV

    Acceso a la webcam con OpenCV

    Conociendo la clase cv::Mat de OpenCV