java.lang.NullPointerException
at java.base/java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:177)
at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.describeBeans(ConfigurationPropertiesReportEndpoint.java:190)
at org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.extract(ConfigurationPropertiesReportEndpoint.java:138)
at org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.configurationProperties(ConfigurationPropertiesReportEndpoint.java:124
Comment From: wilkinsona
Thanks for the pull request, @qxo.
It looks like you have a null @ConfigurationProperties bean but I cannot see how that can happen. Any null @ConfigurationProperties bean in the context doesn't appear to be included in the map that's returned from ConfigurationPropertiesBean.getAll(ConfigurableApplicationContext). Can you please provide a sample or, even better, add a test to ConfigurationPropertiesReportEndpointTests that reproduces the problem?
I'd like to fully understand the problem as, if the change that you have proposed is needed, I suspect a similar change is also needed in ConfigurationPropertiesReportEndpoint.configurationPropertiesWithPrefix(String).
Comment From: qxo
Thanks for the pull request, @qxo.
It looks like you have a
null@ConfigurationPropertiesbean but I cannot see how that can happen. Anynull@ConfigurationPropertiesbean in the context doesn't appear to be included in the map that's returned fromConfigurationPropertiesBean.getAll(ConfigurableApplicationContext). Can you please provide a sample or, even better, add a test toConfigurationPropertiesReportEndpointTeststhat reproduces the problem?I'd like to fully understand the problem as, if the change that you have proposed is needed, I suspect a similar change is also needed in
ConfigurationPropertiesReportEndpoint.configurationPropertiesWithPrefix(String).
I'm not sure how to reproduce the problem. It's fix our private project.
I'll try reproduce the problem in testcase:)
Comment From: snicoll
@qxo how is it going?
Comment From: qxo
@qxo how is it going?
see the poc: https://github.com/qxo/springboot-actuator-configprops-npe-poc
-
ConfigurationPropertiesBean.getAllshould ignored the null value (ConfigurationPropertiesBean) in the return Map. -
In
@ConfigurationPropertiesbean class some@beando not have ConfigurationProperties annotation will trigger the issue.
Comment From: wilkinsona
Thanks for the sample. Unfortunately, it doesn't reproduce the problem.
Accessing http://127.0.0.1:8081/actuator/configprops results in a 404 response as there's no actuator dependency. After adding a dependency on spring-boot-starter-actuator, accessing http://127.0.0.1:8081/actuator/configprops results in a 200 response with no sign of the NPE.
The code in the sample is also quite unusual as @Configuration and @ConfigurationProperties should not be used on the same class.
Can you please update the sample so that it reproduces the NPE?
Comment From: wilkinsona
It's been a couple of weeks now. Closing due to the lack of requested feedback. If you find the time to update the sample so that it reproduces the problem, we can take another look.
Comment From: qxo
It's been a couple of weeks now. Closing due to the lack of requested feedback. If you find the time to update the sample so that it reproduces the problem, we can take another look.
please check the repo again I was forget the commit the feedback to the pull:(
Comment From: wilkinsona
Thanks for the updated sample. I've reproduced the problem now. As suspected, it occurs with both http://127.0.0.1:8081/actuator/configprops and http://127.0.0.1:8081/actuator/configprops/example.property.
Before we decide what to do here, I think we need to understand why we're getting null beans. Filtering them out may not be the right place to address the problem.
Comment From: wilkinsona
This looks like a Framework bug to me. When a bean is defined in a static method, Framework considers the bean to be annotated with annotations on the configuration class. The same is not true when the bean is defined in an instance method.
The following tests illustrate this behavior:
package com.example;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
class FindAnnotationOnBeanTests {
@Test
void beanDefinedInInstanceMethodDoesNotHaveAnnotationsFromItsConfigurationClass() {
beanDoesNotHaveAnnotationsFromItsConfigurationClass(InstanceBeanMethodConfiguration.class);
}
@Test
void beanDefinedInStaticMethodDoesNotHaveAnnotationsFromItsConfigurationClass() {
beanDoesNotHaveAnnotationsFromItsConfigurationClass(StaticBeanMethodConfiguration.class);
}
void beanDoesNotHaveAnnotationsFromItsConfigurationClass(Class<?> config) {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(config)) {
ExampleAnnotation annotation = context.getBeanFactory().findAnnotationOnBean("exampleBean",
ExampleAnnotation.class);
assertThat(annotation).isNull();
}
}
@Configuration
@ExampleAnnotation
static class StaticBeanMethodConfiguration {
@Bean
static String exampleBean() {
return "example";
}
}
@Configuration
@ExampleAnnotation
static class InstanceBeanMethodConfiguration {
@Bean
String exampleBean() {
return "example";
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
static @interface ExampleAnnotation {
}
}
beanDefinedInInstanceMethodDoesNotHaveAnnotationsFromItsConfigurationClass passes but beanDefinedInStaticMethodDoesNotHaveAnnotationsFromItsConfigurationClass fails.
I'll ask the Framework team to take a look.
Comment From: wilkinsona
The Framework team agree that the behavior of findAnnotationOnBean is a bug. However, it has behaved as it currently does for over 10 years so they understandably don't want to change it in 5.x. We'll have to work around the current behaviour.
Comment From: wilkinsona
Thanks very much, @qxo.