NPE at startup when using Spring Native
Closed this issue · 3 comments
I'm using Spring Boot 3 with Liquibase and liquibase-sessionlock
. It works well.
Then I'm trying to build a Spring Native docker image as described here: https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html#native-image.developing-your-first-application.buildpacks.gradle
The image is built successfully.
But when I run it an NPE error occurs:
2023-03-22T17:07:26.914Z ERROR 1 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase': java.lang.NullPointerException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1762) ~[org.myapp.MyApplication:6.0.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[org.myapp.MyApplication:6.0.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[org.myapp.MyApplication:6.0.6]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[org.myapp.MyApplication:6.0.6]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[org.myapp.MyApplication:6.0.6]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[org.myapp.MyApplication:6.0.6]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[org.myapp.MyApplication:6.0.6]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:313) ~[org.myapp.MyApplication:6.0.6]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[org.myapp.MyApplication:6.0.6]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1132) ~[org.myapp.MyApplication:6.0.6]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:907) ~[org.myapp.MyApplication:6.0.6]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584) ~[org.myapp.MyApplication:6.0.6]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[org.myapp.MyApplication:3.0.4]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) ~[org.myapp.MyApplication:3.0.4]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) ~[org.myapp.MyApplication:3.0.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[org.myapp.MyApplication:3.0.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1304) ~[org.myapp.MyApplication:3.0.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1293) ~[org.myapp.MyApplication:3.0.4]
at org.myapp.MyApplication.main(MyApplication.java:12) ~[org.myapp.MyApplication:na]
Caused by: liquibase.exception.LiquibaseException: java.lang.NullPointerException
at liquibase.Liquibase.runInScope(Liquibase.java:2452) ~[na:na]
at liquibase.Liquibase.update(Liquibase.java:236) ~[na:na]
at liquibase.Liquibase.update(Liquibase.java:221) ~[na:na]
at liquibase.integration.spring.SpringLiquibase.performUpdate(SpringLiquibase.java:328) ~[org.myapp.MyApplication:na]
at liquibase.integration.spring.SpringLiquibase.afterPropertiesSet(SpringLiquibase.java:283) ~[org.myapp.MyApplication:na]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1808) ~[org.myapp.MyApplication:6.0.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1758) ~[org.myapp.MyApplication:6.0.6]
... 18 common frames omitted
Caused by: java.lang.NullPointerException: null
at java.base@17.0.6/java.lang.reflect.Method.invoke(Method.java:561) ~[org.myapp.MyApplication:na]
at com.github.blagerweij.sessionlock.SessionLockService.lambda$static$1(SessionLockService.java:257) ~[org.myapp.MyApplication:na]
at com.github.blagerweij.sessionlock.SessionLockService.getLog(SessionLockService.java:267) ~[org.myapp.MyApplication:na]
at com.github.blagerweij.sessionlock.SessionLockService.acquireLock(SessionLockService.java:156) ~[org.myapp.MyApplication:na]
at com.github.blagerweij.sessionlock.SessionLockService.waitForLock(SessionLockService.java:77) ~[org.myapp.MyApplication:na]
at liquibase.Liquibase.lambda$update$1(Liquibase.java:239) ~[na:na]
at liquibase.Scope.lambda$child$0(Scope.java:180) ~[org.myapp.MyApplication:na]
at liquibase.Scope.child(Scope.java:189) ~[org.myapp.MyApplication:na]
at liquibase.Scope.child(Scope.java:179) ~[org.myapp.MyApplication:na]
at liquibase.Scope.child(Scope.java:158) ~[org.myapp.MyApplication:na]
at liquibase.Liquibase.runInScope(Liquibase.java:2447) ~[na:na]
... 24 common frames omitted
Tested with the following framework & library versions:
Spring Boot: 3.0.4
org.graalvm.buildtools.native
: 0.9.20
liquibase-core
: 4.17.2
liquibase-sessionlock
: 1.6.2
Since the SessionLockService
implementation uses reflection, you must follow the guidance to "provide your own hints for reflection" - see https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html#native-image.advanced.custom-hints.
You would need something like:
import liquibase.Scope;
import liquibase.logging.LogService;
import org.springframework.util.ReflectionUtils;
...
public class LiquibaseSessionLockRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
// Register method for reflection
Method scopeMethod = ReflectionUtils.findMethod(Scope.class, "getLog", Class.class);
if (scopeMethod != null) {
hints.reflection().registerMethod(scopeMethod, ExecutableMode.INVOKE);
}
Method logServiceMethod = ReflectionUtils.findMethod(LogService.class, "getLog", Class.class);
if (logServiceMethod != null) {
hints.reflection().registerMethod(logServiceMethod, ExecutableMode.INVOKE);
}
}
}
With this class added, and @ImportRuntimeHints
annotation added to your @SpringBootApplication
class, the NPE should disappear.
The reason why we're using that ugly reflection code in the first place is to support the different Liquibase releases (3.x, 4.x). It was hard to get hold of the LogService in a way that would work with all releases. I can see how this would break on a GraalVM, let me see if we can find an alternative approach.
Should be resolved in v1.6.4