Hello team,
I'm currently using Spring Boot 2.3.5.RELEASE and I'm trying to provide property via environment variable but the Spring boot app fails to start with the following exception: `
APPLICATION FAILED TO START
Description:
Failed to bind properties under "vault" to com.example.relaxedbinding.Configuration:
Property: vault.token-runtime
Value: 242342
Origin: System Environment Property "VAULT_TOKEN_RUNTIME"
Reason: Failed to bind properties under 'vault' to com.example.relaxedbinding.Configuration
Action:
Update your application's configuration `
The weird thing is that it does not fail on each run. The failure rate is 30%-ish.
Here is a link to my githup repo sample project: https://github.com/emilnkrastev/relaxed-binding
Step to reproduce the issue:
1. export VAULT_TOKEN_RUNTIME=242342
2. mvn clean install
3. java -jar target/relaxed-binding-0.0.1-SNAPSHOT.jar
4. If it does not fail go to step 3 and try again.
Do you have an idea what is wrong with the relaxed binding?
Regards!
Comment From: wilkinsona
Thanks for the sample.
You currently have two ways of getting the value of your token-runtime
property. The getTokenRuntime()
method and getToken().getRuntime()
. The former fails with a NullPointerException
if setToken(Token)
hasn't been called first. You can see this NPE by running your app with --debug
:
Caused by: java.lang.IllegalStateException: Unable to get value for property token-runtime
at org.springframework.boot.context.properties.bind.JavaBeanBinder$BeanProperty.lambda$getValue$0(JavaBeanBinder.java:336) ~[spring-boot-2.3.5.RELEASE.jar!/:2.3.5.RELEASE]
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:100) ~[spring-boot-2.3.5.RELEASE.jar!/:2.3.5.RELEASE]
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:80) ~[spring-boot-2.3.5.RELEASE.jar!/:2.3.5.RELEASE]
at org.springframework.boot.context.properties.bind.JavaBeanBinder.bind(JavaBeanBinder.java:56) ~[spring-boot-2.3.5.RELEASE.jar!/:2.3.5.RELEASE]
at org.springframework.boot.context.properties.bind.Binder.lambda$bindDataObject$5(Binder.java:451) ~[spring-boot-2.3.5.RELEASE.jar!/:2.3.5.RELEASE]
at org.springframework.boot.context.properties.bind.Binder$Context.withIncreasedDepth(Binder.java:571) ~[spring-boot-2.3.5.RELEASE.jar!/:2.3.5.RELEASE]
at org.springframework.boot.context.properties.bind.Binder$Context.withDataObject(Binder.java:557) ~[spring-boot-2.3.5.RELEASE.jar!/:2.3.5.RELEASE]
at org.springframework.boot.context.properties.bind.Binder$Context.access$300(Binder.java:512) ~[spring-boot-2.3.5.RELEASE.jar!/:2.3.5.RELEASE]
at org.springframework.boot.context.properties.bind.Binder.bindDataObject(Binder.java:449) ~[spring-boot-2.3.5.RELEASE.jar!/:2.3.5.RELEASE]
at org.springframework.boot.context.properties.bind.Binder.bindObject(Binder.java:390) ~[spring-boot-2.3.5.RELEASE.jar!/:2.3.5.RELEASE]
at org.springframework.boot.context.properties.bind.Binder.bind(Binder.java:319) ~[spring-boot-2.3.5.RELEASE.jar!/:2.3.5.RELEASE]
... 46 common frames omitted
Caused by: java.lang.reflect.InvocationTargetException: null
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_252]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_252]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_252]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_252]
at org.springframework.boot.context.properties.bind.JavaBeanBinder$BeanProperty.lambda$getValue$0(JavaBeanBinder.java:333) ~[spring-boot-2.3.5.RELEASE.jar!/:2.3.5.RELEASE]
... 56 common frames omitted
Caused by: java.lang.NullPointerException: null
at com.example.relaxedbinding.Configuration$Token.access$000(Configuration.java:81) ~[classes!/:0.0.1-SNAPSHOT]
at com.example.relaxedbinding.Configuration.getTokenRuntime(Configuration.java:38) ~[classes!/:0.0.1-SNAPSHOT]
... 61 common frames omitted
Java reflection returns a class's methods in an unspecified and variable order. It's this variable order that causes the failure to be intermittent – it only happens when getTokenRuntime()
is returned and therefore called before setToken(Token)
.
You can fix your problem by removing your getTokenRuntime()
method.
We'll keep this issue open to look at making the ordering in which the methods are processed deterministic. We may be able to achieve this by sorting them by their names.
Comment From: wilkinsona
Framework's configuration class parsing uses ASM to ensure consistent @Bean
method ordering. I don't think we should use ASM, but there is some precedent for trying to make the order of the class's methods stable.