Describe the bug
In my Spring Boot project I got a custom metrics configuration, that
a) in constructor adds a DiskSpaceMetric
b) creates a MeterRegistryCustomizer<MeterRegistry> bean
a) produces a circular dependency error b) produces an NPE, caused by the same circle
The injected MeterRegistry waits for the configuration, which in turn waits for the MeterRegistry to finish initialization.
I don't see how this can be solved by anything I can do, so there seems to be an issue in micrometer itself.
Given that locally adding @Lazy helps, but on production server doesn't, it seems to be race condition.
Environment - OS: Linux - Java version: 11 - Sping Boot 2.6.1
To Reproduce
@Configuration
public class MetricsConfig {
public MetricsConfig(final MeterRegistry aMeterRegistry) {}//@Lazy on parameter _sometimes_ helps
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return tRegistry -> tRegistry.config();
}
}
Constructor active causes
Description:
The dependencies of some of the beans in the application context form a cycle:
webMvcMetricsFilter defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfiguration.class]
┌─────┐
| prometheusMeterRegistry defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusMetricsExportAutoConfiguration.class]
↑ ↓
| metricsConfig defined in file [/foo/bar/MetricsConfig.class]
└─────┘
Constructor removed, metricsCommonTags causes (basically the same issue)
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:163) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:577) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:302) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290) ~[spring-boot-2.6.1.jar:2.6.1]
at foo.MyApplication.main(MyApplication.java:12) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-2.6.1.jar:2.6.1]
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:142) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:104) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:473) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:206) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:182) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:160) ~[spring-boot-2.6.1.jar:2.6.1]
... 13 common frames omitted
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webMvcMetricsFilter' defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfiguration.class]: Unsatisfied dependency expressed through method 'webMvcMetricsFilter' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'prometheusMeterRegistry' defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusMetricsExportAutoConfiguration.class]: Initialization of bean failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:541) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:212) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:203) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addServletContextInitializerBeans(ServletContextInitializerBeans.java:97) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.web.servlet.ServletContextInitializerBeans.<init>(ServletContextInitializerBeans.java:86) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getServletContextInitializerBeans(ServletWebServerApplicationContext.java:260) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize(ServletWebServerApplicationContext.java:234) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.web.embedded.tomcat.TomcatStarter.onStartup(TomcatStarter.java:53) ~[spring-boot-2.6.1.jar:2.6.1]
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5219) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1396) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1386) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:140) ~[na:na]
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:919) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:835) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1396) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1386) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:140) ~[na:na]
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:919) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:263) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:432) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:927) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.apache.catalina.startup.Tomcat.start(Tomcat.java:486) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:123) ~[spring-boot-2.6.1.jar:2.6.1]
... 18 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'prometheusMeterRegistry' defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusMetricsExportAutoConfiguration.class]: Initialization of bean failed; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:628) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791) ~[spring-beans-5.3.13.jar:5.3.13]
... 58 common frames omitted
Caused by: java.lang.NullPointerException: null
at java.base/java.util.Objects.requireNonNull(Objects.java:221) ~[na:na]
at io.micrometer.core.instrument.ImmutableTag.<init>(ImmutableTag.java:35) ~[micrometer-core-1.8.0.jar:1.8.0]
at io.micrometer.core.instrument.Tag.of(Tag.java:29) ~[micrometer-core-1.8.0.jar:1.8.0]
at io.micrometer.core.instrument.Tags.of(Tags.java:254) ~[micrometer-core-1.8.0.jar:1.8.0]
at io.micrometer.core.instrument.MeterRegistry$Config.commonTags(MeterRegistry.java:748) ~[micrometer-core-1.8.0.jar:1.8.0]
at foo.bar.MetricsConfig.lambda$1(MetricsConfig.java:46) ~[classes/:na]
at org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryConfigurer.lambda$customize$0(MeterRegistryConfigurer.java:78) ~[spring-boot-actuator-autoconfigure-2.6.1.jar:2.6.1]
at org.springframework.boot.util.LambdaSafe$Callbacks.lambda$null$0(LambdaSafe.java:287) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.util.LambdaSafe$LambdaSafeCallback.invoke(LambdaSafe.java:159) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.util.LambdaSafe$Callbacks.lambda$invoke$1(LambdaSafe.java:286) ~[spring-boot-2.6.1.jar:2.6.1]
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541) ~[na:na]
at org.springframework.boot.util.LambdaSafe$Callbacks.invoke(LambdaSafe.java:286) ~[spring-boot-2.6.1.jar:2.6.1]
at org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryConfigurer.customize(MeterRegistryConfigurer.java:78) ~[spring-boot-actuator-autoconfigure-2.6.1.jar:2.6.1]
at org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryConfigurer.configure(MeterRegistryConfigurer.java:63) ~[spring-boot-actuator-autoconfigure-2.6.1.jar:2.6.1]
at org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryPostProcessor.postProcessAfterInitialization(MeterRegistryPostProcessor.java:64) ~[spring-boot-actuator-autoconfigure-2.6.1.jar:2.6.1]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:455) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1808) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) ~[spring-beans-5.3.13.jar:5.3.13]
... 68 common frames omitted
It somehow seems to be related to
21134
22926
Comment From: snicoll
Thanks for the report but assigning the MeterRegistry in the configuration class is probably what triggers it. I don't believe this to be a bug but rather a consequence of the arrangement above.
Have you tried the following instead?
@Configuration
public class MetricsConfig {
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags(MeterRegistry meterRegistry) {
return tRegistry -> tRegistry.config();
}
}
I don't believe either that the two issues you've linked are related.
Comment From: 2019-05-10
Well, that brings me back to the same issue as with the constructor:
The dependencies of some of the beans in the application context form a cycle:
webMvcMetricsFilter defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfiguration.class]
┌─────┐
| prometheusMeterRegistry defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusMetricsExportAutoConfiguration.class]
↑ ↓
| metricsCommonTags defined in class path resource [foo/bar/MetricsConfig.class]
└─────┘
Bean definition followed the example given here, btw: https://docs.spring.io/spring-boot/docs/2.4.12/reference/html/production-ready-features.html
The issues I listed seem to deal with the order of initialization, and at least here the PrometheusMetricsExportAutoConfiguration would need to be done before my custom configuration.
Why it does wait for my custom configuration instead, I can't really figure out.
Since it is clear that my custom config needs the PrometheusMetricsExportAutoConfiguration to be finished, the order of initialization should be rather obvious.
Comment From: snicoll
Sorry, it happens often that I overlook something when looking at code in text, rather than in my IDE. The circle is logical as you're trying to customize something and inject it at the same time. It is impossible for the framework to resolve that cycle.
Why do you need to inject the registry in your registry customizer?
Comment From: 2019-05-10
Because I
a) need to add another MeterBinder
b) customize the common tags of the registry
The way I do it is, from everything I've seen, the canonical way to do it. Maybe I am too dim, but what other way is there to achieve that?
"trying to customize something and inject it at the same time." But if I don't inject it, it's still null nevertheless:
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return tRegistry -> tRegistry.config(); //NPE since tRegistry is null
}
Assuming I leave out for the monent the constructor, the MeterRegistry is still not available, since apparently the PrometheusMetricsExportAutoConfiguration waits for this configuration to finish.
(And, btw, I always presumed, injection requires the injected instance to be, well, instantiated already.)
Given that adding @Lazy sometimes works, apparently the framework is able to resolve that cycle somehow:
public MetricsConfig(@Lazy final MeterRegistry aMeterRegistry) {}
Comment From: snicoll
The way I do it is, from everything I've seen, the canonical way to do it.
Injecting MeterRegistry in a MeterRegistryCustomizer? Where have you seen that?
But if I don't inject it, it's still null nevertheless:
I don't see how that could happen. If you want support from us, please share a small sample that we can run ourselves that reproduce what you've described. You can do so by attaching a zip to this issue or sharing a link to a GitHub repository. Thanks.
Comment From: 2019-05-10
I' ve got then impression we're talking at cross-purposes here. IMO this is a race condition. The class i provide is really the smallest possible example to reproduce the issue.
But let's make it simple:
how am I supposed to
a) add a custom MeterBinder
b) create a MeterRegistryCustomizer<MeterRegistry> like this one
// https://docs.spring.io/spring-boot/docs/2.4.12/reference/html/production-ready-features.html#production-ready-metrics-getting-started
@Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags("region", "us-east-1");
}
? If I do it wrong, there needs to be a right way -- if that way works, I close the issue. If you cannot provide a different way, I take it as confirmation that this is, in fact, a bug.
Comment From: snicoll
The code above is tested in various places so it works yes. The reason why I am asking for a sample is because there is something you're not showing and I am not keen to have a ping-pong conversation here. If you share a sample, I am more than happy to look at it.
Comment From: 2019-05-10
I have no idea, what I am "not showing" and why that very small yet complete class does not satisfy whatever you need. But neither can you show a different way, so obviously there's no other way and we'rer facing a bug here.
Comment From: snicoll
Please be considerate of our time. We've already discussed that injecting MeterRegistry is never going to work, so the configuration class you've shown doesn't make a sample for "But if I don't inject it, it's still null nevertheless". If you want support, please share a sample we can run (an actual project that we can run ourselves, not code in text).
Comment From: spring-projects-issues
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.
Comment From: 2019-05-10
I provided everything I have. I have explained that the same piece of code sometimes works, sometimes not. @snicoll told me twice, it should work and twice, it couldn't work -- of the exactly, unchanged piece of code.
Since both Springboot on my systems and @snicoll here at GitHup seem to have the same opinion about its sometimes workings, it's pretty clear that everything is provided.
I, at least, have no idea what else I could do to help you to make the selfsame conclusion explicitly @snicoll implicitly expressed already :shrug:
Comment From: snicoll
Don't twist my words please. I've said the example in the reference doc works. None of those injects a MeterRegistry anywhere as we've established several times here. You are, so there's quite a difference (and the reason you get a cycle in the first place).
If you don't want to take the time to share a small sample, we can't justify spending time on this so I am going to close this now. If you share a sample that demonstrates what you're trying to do we can reconsider.
Comment From: wilkinsona
@jivkoto You should not be injecting a MeterRegistry into a MeterBinder. The latter is called as part of initialising the former so the injection attempt creates a cycle.
The binder’s bindTo method will be called with a MeterRegistry so the injection isn’t needed and should be removed:
@Bean
MeterBinder queueSize() {
return (registry) -> Gauge.builder("queueSize", ___some_supplier_here___).register(registry);
}
Comment From: jivkoto
@wilkinsona Not sure how you saw my comment as I removed it right after I figured out that I should not pass MeterRegistry as it will be provided by the MeterBinder, exactly how you described it.
Anyway, let just add some value to also comment that this is the case with the initial problem described at the begging. That guy should not have constructor @Autowire aMeterRegistry
the public MetricsConfig(final MeterRegistry aMeterRegistry)
It can't construct the MetricsConfig as MeterRegistry is not ready yet. And it can't be ready as the MetricsConfig class produces MeterRegistryCustomizer<MeterRegistry> that is part of MeterRegistry configuration.
Probably he also needs to use MeterRegistry for some meters. And the solution would be to remove it from the constructor and to only @Autowired on the concerned @Bean methods.
@wilkinsona Thanks a lot. Your comment was right on the spot.