Hello,
After building the native image in spring boot 3.0.2, there are problems with beans annotated with @Validated.
The following exception is thrown, for instance, for the fresh spring cloud project (initialzr -> spring 3.0.2, graalvm, cloud gateway, security).
It runs without any problem on the normal JVM (even with spring.aot.enabled=true what unfortunately does not surprise me, since it did not change anything in none of my tests).
Caused by: org.springframework.boot.context.properties.ConfigurationPropertiesBindException: Error creating bean with name 'gatewayProperties': Could not bind properties to 'GatewayProperties' : prefix=spring.cloud.gateway, ignoreInvalidFields=false, ignoreUnknownFields=true
2023-01-26T20:38:17.644912100Z at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.bind(ConfigurationPropertiesBindingPostProcessor.java:99) ~[com.example.spring.gateway.ApiGatewayApplicationKt:3.0.2]
2023-01-26T20:38:17.644917000Z at org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.postProcessBeforeInitialization(ConfigurationPropertiesBindingPostProcessor.java:79) ~[com.example.spring.gateway.ApiGatewayApplicationKt:3.0.2]
2023-01-26T20:38:17.644922000Z at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:420) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T20:38:17.644926900Z at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1743) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T20:38:17.644932000Z at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T20:38:17.644936700Z at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T20:38:17.644941500Z at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T20:38:17.644947300Z at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T20:38:17.644952200Z at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T20:38:17.644956900Z at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T20:38:17.644961500Z at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T20:38:17.644966500Z at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1405) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T20:38:17.644972000Z at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1325) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T20:38:17.644976900Z at org.springframework.beans.factory.aot.BeanInstanceSupplier.resolveArgument(BeanInstanceSupplier.java:334) ~[na:na]
2023-01-26T20:38:17.644985200Z ... 21 common frames omitted
2023-01-26T20:38:17.644989600Z Caused by: jakarta.validation.ValidationException: HV000183: Unable to initialize 'jakarta.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead
2023-01-26T20:38:17.644994600Z at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:211) ~[na:na]
I mentioned it also in https://github.com/spring-cloud/spring-cloud-gateway/issues/2802#issuecomment-1405628494 .
That exception is also thrown on JVM, but it is swallowed on some step, whereas it is not in the native run - some try catch is missing, maybe connected with @ConfigurationProperties processing.
Also, I faced another problem when I replaced every bean with that annotation, which is connected with ValidationAutoConfiguration
Caused by: jakarta.validation.ValidationException: HV000183: Unable to initialize 'jakarta.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead
2023-01-26T21:20:21.808504200Z at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:211) ~[na:na]
2023-01-26T21:20:21.808508600Z at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.<init>(ResourceBundleMessageInterpolator.java:97) ~[na:na]
2023-01-26T21:20:21.808512600Z at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolator(AbstractConfigurationImpl.java:575) ~[com.example.spring.gateway.ApiGatewayApplicationKt:8.0.0.Final]
2023-01-26T21:20:21.808516400Z at org.springframework.boot.validation.MessageInterpolatorFactory.getMessageInterpolator(MessageInterpolatorFactory.java:79) ~[na:na]
2023-01-26T21:20:21.808519500Z at org.springframework.boot.validation.MessageInterpolatorFactory.getObject(MessageInterpolatorFactory.java:70) ~[na:na]
2023-01-26T21:20:21.808523000Z at org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration.defaultValidator(ValidationAutoConfiguration.java:64) ~[com.example.spring.gateway.ApiGatewayApplicationKt:na]
2023-01-26T21:20:21.808531400Z at org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration__BeanDefinitions.lambda$getDefaultValidatorInstanceSupplier$0(ValidationAutoConfiguration__BeanDefinitions.java:33) ~[na:na]
2023-01-26T21:20:21.808535000Z at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:68) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T21:20:21.808538100Z at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:54) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T21:20:21.808554900Z at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$get$2(BeanInstanceSupplier.java:208) ~[na:na]
2023-01-26T21:20:21.808558000Z at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:59) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T21:20:21.808597500Z at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:47) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T21:20:21.808631500Z at org.springframework.beans.factory.aot.BeanInstanceSupplier.invokeBeanSupplier(BeanInstanceSupplier.java:220) ~[na:na]
2023-01-26T21:20:21.808636000Z at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:208) ~[na:na]
2023-01-26T21:20:21.808640500Z at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1225) ~[com.example.spring.gateway.ApiGatewayApplicationKt:6.0.4]
2023-01-26T21:20:21.808643900Z ... 18 common frames omitted
2023-01-26T21:20:21.808646900Z Caused by: java.lang.NoClassDefFoundError: jakarta.el.ELManager
2023-01-26T21:20:21.808649900Z at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:194) ~[na:na]
2023-01-26T21:20:21.808653100Z ... 32 common frames omitted
2023-01-26T21:20:21.808656100Z
BTW does org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration should be possible to exclude via springautoconfigure.exclude? It is annotated with @AutoConfiguration, but it even runs on JVM when excluded there...
Comment From: wilkinsona
I don't think we need a Spring Boot issue for this in addition to the existing Spring Cloud issue. @OlgaMaciaszek has re-opened the Spring Cloud issue and is investigating. Closing in favour of the Cloud issue for now at least.
Comment From: wilkinsona
We have a smoke test for validation that covers @Validated on a @ConfigurationProperties class that is passing. Therefore, we know that this works in the general case. I suspect there's something different about Spring Cloud or your application which is causing a failure. Olga's investigating that and there's no point in us duplicating her effort. If she discovers a problem in Boot we can re-open this issue to fix it. Until then, please be patient and allow Olga to look into things.
Comment From: wilkinsona
https://github.com/spring-cloud/spring-cloud-gateway/issues/2802#issuecomment-1405628494 describes multiple problems and Olga's going to focus on the ReactiveOAuth2AuthorizedClientManager side of things.
@Azbesciak As I said above, we believe that validation of configuration properties works in a native image. If you'd like us to investigate why it is not working for you, please provide a minimal sample that reproduces the NoClassDefFoundError for jakarta.el.ELManager. You can share such a sample with us by zipping it up and attaching it to this issue or by pushing it to a separate repository on Github.
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: Azbesciak
From boot 3.0.5, gateway 2022.0.2 and during processAot:
Exception in thread "main" java.lang.ExceptionInInitializerError
at org.springframework.validation.beanvalidation.BeanValidationBeanRegistrationAotProcessor.processAheadOfTime(BeanValidationBeanRegistrationAotProcessor.java:67)
at org.springframework.beans.factory.aot.BeanDefinitionMethodGeneratorFactory.getAotContributions(BeanDefinitionMethodGeneratorFactory.java:151)
at org.springframework.beans.factory.aot.BeanDefinitionMethodGeneratorFactory.getBeanDefinitionMethodGenerator(BeanDefinitionMethodGeneratorFactory.java:99)
at org.springframework.beans.factory.aot.BeanDefinitionMethodGeneratorFactory.getBeanDefinitionMethodGenerator(BeanDefinitionMethodGeneratorFactory.java:115)
at org.springframework.beans.factory.aot.BeanRegistrationsAotProcessor.processAheadOfTime(BeanRegistrationsAotProcessor.java:48)
at org.springframework.beans.factory.aot.BeanRegistrationsAotProcessor.processAheadOfTime(BeanRegistrationsAotProcessor.java:36)
at org.springframework.context.aot.BeanFactoryInitializationAotContributions.getContributions(BeanFactoryInitializationAotContributions.java:67)
at org.springframework.context.aot.BeanFactoryInitializationAotContributions.<init>(BeanFactoryInitializationAotContributions.java:49)
at org.springframework.context.aot.BeanFactoryInitializationAotContributions.<init>(BeanFactoryInitializationAotContributions.java:44)
at org.springframework.context.aot.ApplicationContextAotGenerator.lambda$processAheadOfTime$0(ApplicationContextAotGenerator.java:58)
at org.springframework.context.aot.ApplicationContextAotGenerator.withCglibClassHandler(ApplicationContextAotGenerator.java:67)
at org.springframework.context.aot.ApplicationContextAotGenerator.processAheadOfTime(ApplicationContextAotGenerator.java:53)
at org.springframework.context.aot.ContextAotProcessor.performAotProcessing(ContextAotProcessor.java:106)
at org.springframework.context.aot.ContextAotProcessor.doProcess(ContextAotProcessor.java:84)
at org.springframework.context.aot.ContextAotProcessor.doProcess(ContextAotProcessor.java:49)
at org.springframework.context.aot.AbstractAotProcessor.process(AbstractAotProcessor.java:82)
at org.springframework.boot.SpringApplicationAotProcessor.main(SpringApplicationAotProcessor.java:80)
Caused by: jakarta.validation.ValidationException: HV000183: Unable to initialize 'jakarta.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead
at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:211)
at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.<init>(ResourceBundleMessageInterpolator.java:97)
at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolator(AbstractConfigurationImpl.java:575)
at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolatorConfiguredWithClassLoader(AbstractConfigurationImpl.java:834)
at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getMessageInterpolator(AbstractConfigurationImpl.java:485)
at org.hibernate.validator.internal.engine.ValidatorFactoryImpl.<init>(ValidatorFactoryImpl.java:155)
at org.hibernate.validator.HibernateValidator.buildValidatorFactory(HibernateValidator.java:38)
at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.buildValidatorFactory(AbstractConfigurationImpl.java:453)
at jakarta.validation.Validation.buildDefaultValidatorFactory(Validation.java:103)
at org.springframework.validation.beanvalidation.BeanValidationBeanRegistrationAotProcessor$BeanValidationDelegate.getValidatorIfAvailable(BeanValidationBeanRegistrationAotProcessor.java:81)
at org.springframework.validation.beanvalidation.BeanValidationBeanRegistrationAotProcessor$BeanValidationDelegate.<clinit>(BeanValidationBeanRegistrationAotProcessor.java:76)
... 17 more
Caused by: java.lang.NoClassDefFoundError: jakarta/el/ELManager
Still occurs.
Comment From: scottfrederick
The Spring Cloud Gateway issue is still open, so I wouldn't expect this situation to have changed.
Comment From: OlgaMaciaszek
https://github.com/spring-cloud/spring-cloud-gateway/issues/2802 was closed on 30th Jan.
Comment From: wilkinsona
@Azbesciak As far as I can tell, we're still where we were back in January when I made this comment:
As I said above, we believe that validation of configuration properties works in a native image. If you'd like us to investigate why it is not working for you, please provide a minimal sample that reproduces the
NoClassDefFoundErrorforjakarta.el.ELManager. You can share such a sample with us by zipping it up and attaching it to this issue or by pushing it to a separate repository on Github.
You haven't provided the requested sample so no further progress has been made.
Comment From: vicasong
@Azbesciak As far as I can tell, we're still where we were back in January when I made this comment:
As I said above, we believe that validation of configuration properties works in a native image. If you'd like us to investigate why it is not working for you, please provide a minimal sample that reproduces the for . You can share such a sample with us by zipping it up and attaching it to this issue or by pushing it to a separate repository on Github.
NoClassDefFoundError``jakarta.el.ELManagerYou haven't provided the requested sample so no further progress has been made.
I also encountered the same problem: https://github.com/vicasong/valid-error-demo But I don't want to introduce a class that doesn't use: "jakarta.el.ExpressionFactory"
Comment From: wilkinsona
@vicasong, thanks for the sample. Please open a Spring Framework issue for this. During AOT processing, Framework's BeanValidationBeanRegistrationAotProcessor.BeanValidationDelegate is calling Validation.buildDefaultValidatorFactory().getValidator(). This fails because there's no EL implementation on the classpath and it does not take into account your custom Validator configuration where you've configured the use of a ParameterMessageInterpolator such that EL should not be required.
Comment From: ZadeAbderrahmane
Is there any news? Have you found a solution to the problem? Or at least, do you know where it is? Please share any new information with us. @wilkinsona @vicasong @Azbesciak
Comment From: Azbesciak
I needed to override the whole GatewayAutoConfiguration with dependencies to avoid loading beans validation, same as all beans which require it... for instance HttpClientProperties or GatewayProperties (even in 3.1.x).
Comment From: gabcamilo
I found two ways to address this issue:
Add the jakarta-el dependency seems obvious, but the only version that worked was the one from glassfish:
jakarta-el v4.0.2
The second workaround was to explicitly specify a message interpolator to work along with jakarta Validator, in this case, I used the ParameterMessageInterpolator from hibernate validator that was already being used as validator for the project sent by @vicasong. The code below is an example implementation:
import jakarta. validation.*;
import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;
public class MyValidator {
public void validate() {
ValidatorFactory factory = Validation.byDefaultProvider()
.configure()
.messageInterpolator(new ParameterMessageInterpolator())
.buildValidatorFactory();
Validator validator = factory.usingContext()
.getValidator();
Set<ConstraintViolation<Customer>> violations = validator.validate(bean);
}
}
The main problem to address in this issue was the message interpolator that has been used since Bean Validation v2.0 . Although Hibernate Validator is the most commonly used validator alongside Bean Validator (ref), the default message interpolator is provided by jakarta-el, which was not adequately loaded, leading to the error when instantiating ValidatorFactoryImpl in HibernateValidator.buildValidatorFactory(ConfigurationState configurationState). The error occurs because configurationState.defaultMessageInterpolator value should be an implementation of MessageInterpolator interface but ended up null.
Comment From: Chenramvijay
I have to upgrade mongo drivers to use time series collection in my java application.
build.sbt have below entries
"org.mongodb" % "mongodb-driver-sync" % "4.9.0",
"org.mongodb" % "mongodb-driver-legacy" % "4.9.0",
"dev.morphia.morphia" % "morphia" % "2.4.2",
"dev.morphia.morphia" % "entityscanner-plug" % "1.6.1",
"dev.morphia.morphia" % "logging-slf4j" % "1.6.1",
"dev.morphia.morphia" % "morphia-validation" % "2.4.13",
"jakarta.validation" % "jakarta.validation-api" % "3.1.0",
"org.hibernate.validator" % "hibernate-validator" % "8.0.1.Final",
"jakarta.el" % "jakarta.el-api" % "4.0.0",
"org.glassfish" % "jakarta.el" % "4.0.2" % "test",
"org.glassfish.jaxb" % "jaxb-runtime" % "2.3.1" % "runtime",
I am creating morphia validator as below
morphia = new Morphia(new Mapper(options));
// Configure validator
new ValidationExtension();`
getting below error on run
jakarta.validation.ValidationException: HV000183: Unable to initialize 'jakarta.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead
at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:211)
at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.<init>(ResourceBundleMessageInterpolator.java:97)
at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolator(AbstractConfigurationImpl.java:575)
at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolatorConfiguredWithClassLoader(AbstractConfigurationImpl.java:834)
at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getMessageInterpolator(AbstractConfigurationImpl.java:485)
Caused by: java.lang.NoClassDefFoundError: com/sun/el/ExpressionFactoryImpl
at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:203)
at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.<init>(ResourceBundleMessageInterpolator.java:97)
at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolator(AbstractConfigurationImpl.java:575)
at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getDefaultMessageInterpolatorConfiguredWithClassLoader(AbstractConfigurationImpl.java:834)
at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.getMessageInterpolator(AbstractConfigurationImpl.java:485)
Caused by: java.lang.ClassNotFoundException: com.sun.el.ExpressionFactoryImpl
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:476)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.buildExpressionFactory(ResourceBundleMessageInterpolator.java:203)
at org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator.<init>(ResourceBundleMessageInterpolator.java:97)
on debug I found this exception is coming from - dev.morphia.validation.ValidationExtension class on below code
public ValidationExtension() {
validationFactory = Validation.byDefaultProvider()
.configure()
.buildValidatorFactory();}
this class also have errors regarding interface methods - Class 'ValidationExtension' must either be declared abstract or implement abstract method 'postLoad(Object, DBObject, Mapper)' in 'EntityInterceptor'
any suggestion to solve this please.
Comment From: wilkinsona
@Chenramvijay Judging by your dependencies, your problem doesn't have anything to do with Spring Boot. Please ask on Stack Overflow.