I have a Spring boot 2 gradle project That I hope to deploy as a (non-executable) war file to a tomcat 7 instance (RHEL).
I am receiving a NoClassDefFoundError
when deploying the war to tomcat on the server:
...
Caused by: org.hibernate.cfg.beanvalidation.IntegrationException: Error activating Bean Validation integration
at org.hibernate.cfg.beanvalidation.BeanValidationIntegrator.integrate(BeanValidationIntegrator.java:138)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:281)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:462)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:892)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390)
... 34 more
Caused by: java.lang.NoClassDefFoundError: javax/el/ELManager
at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:88)
at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.<init>(ResourceBundleMessageInterpolator.java:47)
at org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultMessageInterpolator(ConfigurationImpl.java:474)
at org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultMessageInterpolatorConfiguredWithClassLoader(ConfigurationImpl.java:650)
at org.hibernate.validator.internal.engine.ConfigurationImpl.getMessageInterpolator(ConfigurationImpl.java:397)
at org.hibernate.validator.internal.engine.ValidatorFactoryImpl.<init>(ValidatorFactoryImpl.java:183)
at org.hibernate.validator.HibernateValidator.buildValidatorFactory(HibernateValidator.java:38)
at org.hibernate.validator.internal.engine.ConfigurationImpl.buildValidatorFactory(ConfigurationImpl.java:364)
at javax.validation.Validation.buildDefaultValidatorFactory(Validation.java:103)
at org.hibernate.cfg.beanvalidation.TypeSafeActivator.getValidatorFactory(TypeSafeActivator.java:501)
at org.hibernate.cfg.beanvalidation.TypeSafeActivator.activate(TypeSafeActivator.java:84)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.hibernate.cfg.beanvalidation.BeanValidationIntegrator.integrate(BeanValidationIntegrator.java:132)
... 40 more
On inspection I find that the hibernate bean validation library has failed to find this particular class (javax.el.ElManager
) during initialization.
The solution here suggests adding the correct el-api v3.0 dependency to the runtime but I'm not sure this is a wise solution because the el-api v2.2 already exists amongst the shared (providedRuntime
in gradle vernacular) libraries in Tomcat. I'm concerned about ClassLoader
issues if I have 2 versions of the same API (Is this a reasonable concern?).
Upgrading to tomcat 8 is a no-go, as we run tomcat7 on RHEL which currently only supports tomcat 7.
How do I tell Spring to use javax el-api v2.2?
The default tomcat.version
when depending on spring-boot-starter-tomcat seems to be 8.5.31
according to the gradlew dependencies
command:
+--- org.springframework.boot:spring-boot-starter-tomcat:2.0.2.RELEASE
| +--- javax.annotation:javax.annotation-api:1.3.2
| +--- org.apache.tomcat.embed:tomcat-embed-core:8.5.31
| +--- org.apache.tomcat.embed:tomcat-embed-el:8.5.31
| \--- org.apache.tomcat.embed:tomcat-embed-websocket:8.5.31
As I understand from another post, settings the gradle ext['tomcat.version']
property to 7.0.76
(our server's version) resolves these dependencies correctly:
+--- org.springframework.boot:spring-boot-starter-tomcat:2.0.2.RELEASE
| +--- javax.annotation:javax.annotation-api:1.3.2
| +--- org.apache.tomcat.embed:tomcat-embed-core:8.5.31 -> 7.0.76
| +--- org.apache.tomcat.embed:tomcat-embed-el:8.5.31 -> 7.0.76
| \--- org.apache.tomcat.embed:tomcat-embed-websocket:8.5.31 -> 7.0.76
| \--- org.apache.tomcat.embed:tomcat-embed-core:7.0.76
However I still run into this NoClassDefFoundError
. It seems to me that Spring does not recognise the correct api version and continues assuming the ElManager
is present.
Thank you for your time.
After trying to modify the transitive dependencies of the default spring configs (namely hibernate-validator
), I came across documentation for Spring Boot 2's minimum server requirements: Tomcat 8.5.X
. Alongside that, only servlet APIs of 3.1+ are supported
The fundamental problem I am fighting against here - which will undoubtedly produce problems down the line if I changed the dependencies - is that Tomcat 7 is not supported by Spring Boot 2. Any transitive library I downgrade may break some other dependency which requires its updated functionality.
Spring Boot 1.5.X does however support Tomcat 7 and Servlet 3.0 application servers. So the solution is to downgrade to Spring Boot 1.5.X (currently at 1.5.13 at time of writing).
Alternatively - and the solution I will be pressing with the systems team here at my workplace - Is to instead use embedded servlet containers (i.e. tomcat 8.5/9 embedded). Unfortunately for me, That means a long discussion into the modification of existing enterprise processes that assumes a deployment mechanism. But that is besides the point :P
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With