I have implemented a REST service using Jersey and wanted to use Bean Validation. When I add the bean validation maven dependency for Jersey (jersey-bean-validation), the webapp breaks on startup due to an error:
HV000183: Unable to initialize 'jakarta.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead
I searched around and found older solutions mentioning including missing dependencies, such as this question:
javax.validation.ValidationException: HV000183: Unable to load 'javax.el.ExpressionFactory'
but the dependencies are outdated in my case. Also, when looking at the dependency tree:
[INFO] org.example:valtest:war:1.0-SNAPSHOT
[INFO] +- org.glassfish.jersey.containers:jersey-container-servlet:jar:3.0.2:compile
[INFO] | +- org.glassfish.jersey.containers:jersey-container-servlet-core:jar:3.0.2:compile
[INFO] | +- org.glassfish.jersey.core:jersey-common:jar:3.0.2:compile
[INFO] | | +- jakarta.annotation:jakarta.annotation-api:jar:2.0.0:compile
[INFO] | | \- org.glassfish.hk2:osgi-resource-locator:jar:1.0.3:compile
[INFO] | +- org.glassfish.jersey.core:jersey-server:jar:3.0.2:compile
[INFO] | | \- org.glassfish.jersey.core:jersey-client:jar:3.0.2:compile
[INFO] | \- jakarta.ws.rs:jakarta.ws.rs-api:jar:3.0.0:compile
[INFO] +- org.glassfish.jersey.inject:jersey-hk2:jar:3.0.2:compile
[INFO] | +- org.glassfish.hk2:hk2-locator:jar:3.0.1:compile
[INFO] | | +- org.glassfish.hk2.external:aopalliance-repackaged:jar:3.0.1:compile
[INFO] | | +- org.glassfish.hk2:hk2-api:jar:3.0.1:compile
[INFO] | | \- org.glassfish.hk2:hk2-utils:jar:3.0.1:compile
[INFO] | \- org.javassist:javassist:jar:3.25.0-GA:compile
[INFO] \- org.glassfish.jersey.ext:jersey-bean-validation:jar:3.0.2:compile
[INFO] +- jakarta.inject:jakarta.inject-api:jar:2.0.0:compile
[INFO] +- jakarta.validation:jakarta.validation-api:jar:3.0.0:compile
[INFO] +- org.hibernate.validator:hibernate-validator:jar:7.0.0.Final:compile
[INFO] | +- org.jboss.logging:jboss-logging:jar:3.4.1.Final:compile
[INFO] | \- com.fasterxml:classmate:jar:1.5.1:compile
[INFO] +- jakarta.el:jakarta.el-api:jar:4.0.0:compile
[INFO] \- org.glassfish:jakarta.el:jar:4.0.0:compile
I can see at the very bottom that the el-api and el libraries are included as transitive dependencies of the jersey-bean-validation library already.
I put a breakpoint into ResourceBundleMessageInterpolator to find out the cause of this, and when the ExpressionFactory is getting instanciated, an exception is thrown:
java.util.ServiceConfigurationError: jakarta.el.ExpressionFactory: org.apache.el.ExpressionFactoryImpl not a subtype
I don't know where that comes from. I can at least create an ExpressionFactory as expected using:
ExpressionFactory.newInstance()
I have created a very simple example project that uses the same Jersey+Jetty setup with maven that I use in my project: https://www.dropbox.com/s/dcvief9dlqk648m/valtest.zip?dl=0
It can be run with mvn jetty:run
and it should display a message on http://localhost:8080/test.
What am I missing?
tl;dr;
The Jetty Maven plugin pulls in a conflicting dependency. The dependency deals with JSP. If you don't have any need for JSP in your application, I think the following solution should be safe. You just need to exclude the bad dependency
So how the
ExpressionFactory
is found, is through aServiceLoader
. Theorg.glassfish:jakarta.el
jar doesn't have the requiredMETA-INF/services
file to be found through theServiceLoader
. But after theServiceLoader
fails, other options will be tried. So ultimately, the jar'sExpressionFactoryImpl
will be found. But the problem is that the Jetty Maven plugin also pulls in an implementation, which does have theMETA-INF/services
file. So that is the implementation that will be used.After some debugging, and stepping into the ServiceLoader code, I came across this error when the class was found:
So I created a new Maven project and added the jetty-maven-plugin as a dependency. After searching through all the jars, I found the
o.a.e.ExpressionFactoryImpl
class. It was in theapache-el
jar that Jetty pulled in. The weird part is that the class actually does implement thejakarta.el.ExpressionFactory
. I thought that maybe the problem was that it implemented the oldjavax
class, but that wasn't the case. I'm not completely sure why this causes an error. But after getting that out of the way, it began to work.Another option if removing
apache-el
is not desiredOne thing I tried before I got the above solution was to try to manually add the
META-INF/services
file to the theorg.glassfish:jakarta.el
jar. What I did was the followingThis may not be a desirable solution either, but it worked.
If you need the
apache-el
, you can also try to replace it with theorg.glassfish:jakarta.el
and see if that works. I haven't tried it yet, but I'm not sure if Jetty will run into similar problems with a different implementation or not.