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
ExpressionFactoryis found, is through aServiceLoader. Theorg.glassfish:jakarta.eljar doesn't have the requiredMETA-INF/servicesfile to be found through theServiceLoader. But after theServiceLoaderfails, other options will be tried. So ultimately, the jar'sExpressionFactoryImplwill be found. But the problem is that the Jetty Maven plugin also pulls in an implementation, which does have theMETA-INF/servicesfile. 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.ExpressionFactoryImplclass. It was in theapache-eljar 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 oldjavaxclass, 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-elis not desiredOne thing I tried before I got the above solution was to try to manually add the
META-INF/servicesfile to the theorg.glassfish:jakarta.eljar. 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.eland 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.