https://bz.apache.org/bugzilla/show_bug.cgi?id=69262
Bug ID: 69262
Summary: Application can fail to start if both
jakarta.el::jakarta.el-api and
org.apache.tomcat.embed::tomcat-embed-el are on the
classpath
Product: Tomcat 10
Version: 10.1.26
Hardware: All
OS: All
Status: NEW
Severity: normal
Priority: P2
Component: EL
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: ------
If both the jakarta.el::jakarta.el-api and
org.apache.tomcat.embed::tomcat-embed-el JARs are on the classpath, the
application can fail to start.
Both JARs contain a jakarta.el.ExpressionFactory, and if the jakarta.el-api
version gets prioritized higher on the classpath, it will trump the
tomcat-embed-el version. This may manifest differently for each application,
but in our case it manifested as a "java.lang.ClassNotFoundException:
com.sun.el.ExpressionFactoryImpl" at startup.
ref:
https://github.com/jakartaee/expression-language/blob/master/api/src/main/java/jakarta/el/ExpressionFactory.java
ref:
https://github.com/apache/tomcat/blob/main/java/jakarta/el/ExpressionFactory.java
Given that jakarta.el-api is the API spec and tomcat-embed-el is the
implementation, it should be assumed that a) both may be on the classpath and
b) that it is safe for both to be on the classpath (e.g. foo-api and foo-impl).
In reviewing the jakarta.el-api version of the ExpressionFactory, their
documentation outlines a clear workflow of how to tell the Factory what your
implementation class is. I belive the tomcat-embed-el ExpressionFactory should
NOT be an override of the API class, but rather something like
"ExpressionFactoryImpl" which instructions to ExpressionFactory that it should
use ExpressionFactoryImpl.
An example POM below shows a valid use case that triggered this scenario: a
springboot application, running on Tomcat Embed (Jasper), using the JSTL tag
reference implementation. The springboot WAR is generated with jakarta.el-api
prioritized higher than tomcat-embed-el in the "classpath.idx" file, which
determines the classpath order for a springboot application. Adding an
<exclusion /> for "el-api" to "jstl-api" will resolve the issue and allow the
application to start.
e.g.
<dependency>
<!-- Jakarta JSTL (Standard Tag Library) API -->
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<scope>runtime</scope>
<exclusions>
<exclusion>
<!-- Tomcat Jasper includes a jakarta.el.** package
which overrides "jakarta.el-api" -->
<groupId>jakarta.el</groupId>
<artifactId>jakarta.el-api</artifactId>
</exclusion>
</exclusions>
</dependency>
Broken Example:
<?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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.foo</groupId>
<artifactId>bar</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>foo-bar</name>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.2</version>
</parent>
<properties>
<maven.version>3.2.5</maven.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>jakarta.el</groupId>
<artifactId>jakarta.el-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<!-- Currently, Tomcat 10 does not ship with a JSTL
implementation out of the box, use the Glassfish
reference impl:
https://stackoverflow.com/a/4928309/215166 -->
<groupId>org.glassfish.web</groupId>
<artifactId>jakarta.servlet.jsp.jstl</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<!--
The Spring Boot Maven Plugin packages
the final artifact as a runnable springboot WAR and adds the
correct MANIFEST.MF entries to allow
simple startup like: java -jar artifact.war
ref:
https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle/
-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.foo.bar.Application</mainClass>
<layout>WAR</layout>
</configuration>
</plugin>
</plugins>
</build>
</project>
--
You are receiving this mail because:
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]