JSF 3.0 en Tomcat 10 con Java 11

Introducción

En este artículo se especifica como desplegar una aplicación JSF 3.0 en el contenedor de servlets Tomcat 10. La particularidad es que, desde la versión 10, Tomcat da soporte a las versiones Java 8 y superiores. En Java 9 hubo un cambio bastante significativo al quitar unas cuantas librerías y volverlas opcionas y en Java 11 se dio un paso más al haber renombrado completamente las rutas de paquetes javax.* a jakarta.*. Oracle, a pesar de haber hecho público el desarrollo de Java, no hizo lo mismo con su nombre. Esto traerá algún que otro dolor de cabeza que intentaremos solventar en la medida de lo posible.

Para la implementación de JSF 3.0 podremos utilizar Glassfish o Mojarra, hemos utilizado Mojarra simplemente porque es la implementación de Oracle. La implementación de Java utilizada ha sido OpenJDK, AdoptOpenJDK en Windows.

En el proyecto que estoy migrando de JSF 2.x a 3.0 hay librerías de terceros que siguen utilizando el nombre de paquete javax.*, veremos como trabajar con ellas sin tener que recompilar el código.

Finalmente, comentar que cada proyecto tiene sus particularidades. En este artículo se intentará abarcar un caso genérico y los problemas más comunes. Si no encuentras respuesta en el artículo puedes dejar un comentario con tu caso específico para intentar resolverlo.

Versiones y herramientas utilizadas

  • Tomcat 10 (implementa las especificaciones requeridas por Jakarta EE9)
  • Java 11/ OpenJDK (han renombrado el paquete javax.* a jakarta.*)
  • Mojarra 3.0 (implementación JSF de Oracle)
  • CDI 3.0 (requerido implícitamente por las anotaciones de beans)

Como configurar JSF 3.0 en Tomcat 10

Tomcat 10 viene por defecto, entre otras, con las siguientes librerías significativas:

  • el-api (Jakarta Expression Language 4.0)
  • jsp-api.jar (JSP 3.0)
  • servlet-api.jar (Jakarta Servlet 5.0)
ServletJSPELTomcat VersionJava Versions
5.03.04.010.0.x8 and later
4.02.33.09.0.x8 and later
3.12.33.08.5.x7 and later
Especificaciones y distintas versiones de Tomcat. Fuente Apache.org

Para poder hacer uso de JSF 3.0 debemos añadir una serie de librerías que implementen la especificación JSF. Hemos elegido Mojarra.

Fichero pom.xml

Añadimos las siguientes librerías:

<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>jakarta.faces</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>org.jboss.weld.servlet</groupId>
    <artifactId>weld-servlet-shaded</artifactId>
    <version>4.0.0.Final</version>
</dependency>
<dependency>
    <groupId>jakarta.servlet.jsp.jstl</groupId>
    <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
    <version>2.0.0</version>
</dependency>

En caso de tener librerías de terceros que hagan uso del paquete javax.* debemos añadir las librerías anteriores a Java 10, por ejemplo:

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
    </dependency>

Fichero web.xml

El servlet de Jakarta Faces 3.0 ya está registrado y mapeado «implícitamente» en *.jsf, *.faces y /faces/* cuando se ejecuta en un contenedor Jakarta Servlet 5.x. Esto se anulará por completo cuando se registre explícitamente como mostramos en el ejemplo. Aunque el fichero web.xml es opcional es preferible utilizar el patrón de URL *.xhtml a los anteriores por razones de seguridad y claridad.

<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>jakarta.faces.webapp.FacesServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
</servlet-mapping>

Fichero faces-config.xml

Este fichero es completamente opcional, pero si lo hubiese, debe ser compatible con Jakarta Faces 3.0. en caso contrario, Jakarta Faces 3.0 se ejecutará en un modo alternativo «fallback» que coincida con la versión version="x.x" declarada en el elemento raíz.

<?xml version="1.0" encoding="UTF-8"?>
<faces-config
    xmlns="https://jakarta.ee/xml/ns/jakartaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-facesconfig_3_0.xsd"
    version="3.0"
>
    <!-- Your faces config here. -->
</faces-config>

Como activar CDI en JSF 3.0

De forma predeterminada, Jakarta Faces 3.0 se ejecuta en modo Jakarta Faces 2.2 en cuanto a su compatibilidad con CDI. Incluso cuando se utiliza un fichero de configuración faces-config.xml compatible con JSF 2.3 o 3.0, CDI sigue estando en modo compatibilidad. Esto hace que @Inject FacesContext no funcione de forma predeterminada.

Actualmente solo hay una forma de activar CDI en Jakarta Faces 2.3 y 3.0 y se ejecute en el modo esperado. Es necesario colocar la anotación @FacesConfig en alguno de los beans administrados por CDI. Basta colocarlo en uno, por ejemplo, un bean de inicio o de configuración general.

@FacesConfig
@ApplicationScoped
public class ApplicationConfig {
    // ...
}

Como migrar JSF 3.0 a Java 11

Fichero pom.xml

Debemos especificar la versión de Java, especialmente si obtenemos un error del tipo:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project XXX: Fatal error compiling: invalid target release: 11

Basta con modificar nuestro fichero POM para indicar a Maven que utilice la versión 11 de Java.

¿Como hacemos que Maven configure nuestro proyecto para Java 11?, fácil, basta con definirlo en el archivo pom.xml con ayuda del plugin maven-compiler-plugin.

Para usar y configurar este plugin vaya a su archivo pom.xml y agregue.

    <build>
      <plugins>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
         </plugin>
      </plugins>
   </build>

A continuación añadimos la versión de Java deseada.

    <properties>
      <maven.compiler.source>11</maven.compiler.source>
      <maven.compiler.target>11</maven.compiler.target>
    </properties>

Ahora Maven gestionará que versión de Java estamos usando, de modo que podamos hacer uso de las características de Java 11 en el proyecto.

Es importante hacer uso de versiones actualizadas del plugin maven-compiler-plugin. Las versiónes 3.6.x son incompatibles con Java 11, es necesario utilizar las versiones del rango 3.8.x y superiores.

Renombrar javax.* a jakarta.*

Si tienes código «antiguo» en tu aplicación obtendrás errores del tipo:

java.lang.NoClassDefFoundError: javax.servlet.ServletRequestListener
java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet

Esto simplemente indica que tu aplicación ha dejado de ser compatible con Java 11 ya que hace uso del paquete javax.* y en Jakarta EE 9 se hace uso de Java 11. La solución pasa por realizar un «search and replace» en todo tu proyecto y sustituir las entradas javax. por jakarta. en todos tus fichero .java.

En Java 11 se han quitado algunas otras librerías. Por ejemplo, javax.mail.* pasa a ser jakarta.mail.* pero por defecto no estará en la distribución de Java 11. Basta con añadir las librerías opcionales al POM. Algunas de las que he necesitado añadir son:

		<dependency>
			<groupId>jakarta.jws</groupId>
			<artifactId>jakarta.jws-api</artifactId>
			<version>3.0.0</version>
		</dependency>
		<dependency>
			<groupId>jakarta.ejb</groupId>
			<artifactId>jakarta.ejb-api</artifactId>
			<version>4.0.0</version>
		</dependency>
		<dependency>
			<groupId>jakarta.xml.ws</groupId>
			<artifactId>jakarta.xml.ws-api</artifactId>
			<version>3.0.0</version>
		</dependency>
		<dependency>
			<groupId>jakarta.mail</groupId>
			<artifactId>jakarta.mail-api</artifactId>
			<version>2.0.1</version>
		</dependency>
		<dependency>
			<groupId>jakarta.activation</groupId>
			<artifactId>jakarta.activation-api</artifactId>
			<version>2.0.1</version>
		</dependency>

Librerías de terceros

Poco podemos hacer en estos casos. Habrá librerías obsoletas que sigan haciendo uso de los paquetes javax.*. Tendremos que añadir a nuestro POM dichas dependencias para evitar errores de compilación y ejecución de las librerías de terceros. Esto dependerá de tu aplicación pero el consejo es que hagas un esfuerzo en buscar si dichas librerías tienen actualizaciones que las hagan compatibles con Java 11.

Referencias de ayuda a la migración a JSF 3.0

Jakarta 5.0 Servlet Specification

Jakarta Server Faces 3.0

Mojarra 3.0

5 Comments

  1. Cesar says:

    Hola, por favor puedes pasar el proyrcto completo. Gracias

    1. Hola Cesar,
      La información la he sacado de un proyecto externo que está en producción, no lo puedo publicar. Si quieres dime tus dudas e intento ayudarte a resolverlas.

      Un saludo!

  2. ¡Hola!
    Si no queremos hacer uso de las nuevas funcionalidades y simplemente actualizar de Tomcat 9 a 10… ¿mejora el rendimiento? ¿Merece la pena en ese caso?
    ¡Gracias!

    1. Hola,
      Algo hará pero nada notable, el cambio sustancial se hizo en la versión 9 con el protocolo http/2.
      Si quieres mejorar rendimiento tienes que implementar técnicas de caché.
      Los beneficios de pasar de la 9 a la 10 es que estarás alineado con los últimos desarrollos del equipo.
      Ten en cuenta que cada versión de Tomcat, incluida la 8.x, tiene un margen de vida en el cual se le da soporte por determinado tiempo.
      Personalmente haría un intento de pasar de la 9 a la 10 pero acompañando un cambio a Java 11 (si lo usas), así estarás totalmente «jakartizado».

  3. Abel V. says:

    Hola!
    Estoy migrando una aplicación desplegada con Tomcat 7 y jdk 6 a Tomcat 10 con java 8. Esto esto es posible?. Teniendo en cuenta que debo modificar lo mas minimo posible. Alguna sugerencia?
    Saludos!!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *