Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native image - HV000183: Unable to initialize 'jakarta.el.ExpressionFactory'. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead #33999

Closed
Azbesciak opened this issue Jan 27, 2023 · 15 comments
Labels
status: invalid An issue that we don't feel is valid

Comments

@Azbesciak
Copy link

Azbesciak commented Jan 27, 2023

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 spring-cloud/spring-cloud-gateway#2802 (comment) .
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...

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jan 27, 2023
@wilkinsona
Copy link
Member

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.

@wilkinsona wilkinsona closed this as not planned Won't fix, can't repro, duplicate, stale Jan 27, 2023
@wilkinsona wilkinsona added status: duplicate A duplicate of another issue for: external-project For an external project and not something we can fix and removed status: waiting-for-triage An issue we've not yet triaged labels Jan 27, 2023
@wilkinsona
Copy link
Member

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.

@wilkinsona
Copy link
Member

spring-cloud/spring-cloud-gateway#2802 (comment) 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.

@wilkinsona wilkinsona reopened this Jan 27, 2023
@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged and removed status: duplicate A duplicate of another issue for: external-project For an external project and not something we can fix labels Jan 27, 2023
@spring-projects-issues
Copy link
Collaborator

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.

@spring-projects-issues spring-projects-issues added the status: feedback-reminder We've sent a reminder that we need additional information before we can continue label Feb 3, 2023
@wilkinsona wilkinsona closed this as not planned Won't fix, can't repro, duplicate, stale Feb 3, 2023
@wilkinsona wilkinsona added status: invalid An issue that we don't feel is valid and removed status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged status: feedback-reminder We've sent a reminder that we need additional information before we can continue labels Feb 3, 2023
@Azbesciak
Copy link
Author

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.

@scottfrederick
Copy link
Contributor

The Spring Cloud Gateway issue is still open, so I wouldn't expect this situation to have changed.

@OlgaMaciaszek
Copy link
Contributor

spring-cloud/spring-cloud-gateway#2802 was closed on 30th Jan.

@wilkinsona
Copy link
Member

@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 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.

You haven't provided the requested sample so no further progress has been made.

@vicasong
Copy link

@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.ELManager

You 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"

@wilkinsona
Copy link
Member

@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.

@ZadeAbderrahmane
Copy link

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

@Azbesciak
Copy link
Author

Azbesciak commented Nov 26, 2023

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).

@gabcamilo
Copy link

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.

image

image

@Chenramvijay

This comment was marked as off-topic.

@wilkinsona

This comment was marked as off-topic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

9 participants