Hello team,
we are facing a strange NullPointerException during autoconfiguration. The problem only happens in some cloud foundry environments and is not reproducible in others nor locally. Since the NPE only occurs during startup I cannot really debug it. Logging does not reveal any reason.
A work-around is to avoid "resolveOutcomesThreaded", e.g. by reducing the CF memory for the application and thus turning Runtime.getRuntime().availableProcessors() to 1 or by setting -XX:ActiveProcessorCount=1 with the same effect.
I suspect that an uncaught exception is thrown in the ThreadedOutcomesResolver's Runnable. But I cannot capture this and there is no logging. I would suggest capturing any exceptions in the Runnable and logging it at least?
https://github.com/spring-projects/spring-boot/blob/bf2950c045e7464f87b5c819ffe646ca75eb1f14/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java#L146
Spring Boot version 3.3.1
java.lang.NullPointerException: Cannot read the array length because "firstHalf" is null
at org.springframework.boot.autoconfigure.condition.OnClassCondition.resolveOutcomesThreaded(OnClassCondition.java:71)
at org.springframework.boot.autoconfigure.condition.OnClassCondition.getOutcomes(OnClassCondition.java:52)
at org.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition.match(FilteringSpringBootCondition.java:49)
at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$ConfigurationClassFilter.filter(AutoConfigurationImportSelector.java:366)
at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.getAutoConfigurationEntry(AutoConfigurationImportSelector.java:131)
at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$AutoConfigurationGroup.process(AutoConfigurationImportSelector.java:430)
at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGrouping.getImports(ConfigurationClassParser.java:813)
at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorGroupingHandler.processGroupImports(ConfigurationClassParser.java:743)
at org.springframework.context.annotation.ConfigurationClassParser$DeferredImportSelectorHandler.process(ConfigurationClassParser.java:714)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:183)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:417)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:290)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:788)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:606)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352)
at com.sap.crun.frunproxy.Application.main(Application.java:26)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91)
at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53)
at org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:58)
Comment From: claussenj
Hi Andy @wilkinsona,
wow, that was fast! Many thanks for the fix!
regards Jens
Comment From: claussenj
Hi Andy @wilkinsona,
we have now deployed Spring Boot 3.3.2 in the affected environment and the original problem still exists: We get the NPE "because \"firstHalf\" is null" and the process exits but the original exception is not logged.
When running with -XX:ActiveProcessorCount=1 I have found an InterruptedException from kafka while processing some event. The stack trace does not fit, but what if an InterruptedException is thrown ThreadedOutcomesResolver.resolveOutcomes()? Then line 166 is hit and still a NPE can occur.
https://github.com/spring-projects/spring-boot/blob/c7e29b7b1bc570a1862a1aeacba5dd390bf3b5c6/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/OnClassCondition.java#L166
regards Jens