Spring Cloud Config: Bootstrap context not loading profile-specific property files for binding

630 Views Asked by At

Setup

  • Spring Boot 2.6.0
  • Spring Cloud Config 3.1 RC1
  • Apache Maven 3.8.x
  • OpenJDK 11

Overview

I have a multi-module Apache Maven project that is set up with the following modules:

  1. bootstrap: contains a PropertySourceLocator for BootstrapConfiguration, defined in spring.factories file.
  2. starter: depends on bootstrap, and it's a (servlet-based) web application
  3. reference: deploys the starter application using the Maven Cargo plugin, deploying into an Apache Tomcat 9.0.55

Runtime

  • The starter module declares a configuration class, annotated with @PropertySource("wa.properties"). This wa.properties on the classpath of the starter module has a setting: cas.authn.syncope.name=Starter
  • The starter module has a ServletInitializer that sets the spring.config.name property to "wa" when building the spring application.
  • The reference module only has a wa-embedded.properties file on the classpath with a setting: cas.authn.syncope.name=Embedded
  • The reference module starts with the spring activated profiles: embedded,all

Note: the cas.authn.syncope.name is bound to a Java POJO, CasConfigurationProperties, that is annotated with @ConfigurationProperties("cas").

Observation

The following bean in the application exists, simplified for this post:

@Bean
@ConditionalOnMissingBean(name = "something")
@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)
public Something something(ApplicationContext ctx, CasConfigurationProperties cas) {
    ...
}
  • If I look at the contents of cas.getAuthn().getSyncope().getName()), it shows: "Starter"
  • If I look at ctx.getEnvironment().getProperty("cas.authn.syncope.name"), it shows "Embedded".

In other words, property binding used during the bootstrapping process does not match the actual environment for the application's context.

Analysis

  • It appears that when the bootstrap application context is created, wa-embedded.properties, a profile-specific property is not read. In fact, the only property source that is used for binding is wa.properties as part of "localProperties", which I believe comes from @PropertySource("wa.properties"). Nothing else is read or found.

  • Then, property binding takes place binding CasConfigurationProperties and cas.authn.syncope.name initialized from @PropertySource("wa.properties"). The value of this property is set to Starter.

  • Then, the application servlet context is initialized and its environment is post-processed with profiles and the appropriate listener and Spring beans are created. In particular, this bean:

@Bean
@ConditionalOnMissingBean(name = "something")
@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)
public Something something(ApplicationContext ctx, CasConfigurationProperties cas) {
    ...
}

...shows that ctx is the actual application context with an environment that is post-processed via all profiles and shows ctx.getEnvironment().getProperty("cas.authn.syncope.name") as "Embedded".

However, CasConfigurationProperties was processed using the Bootstrap context only, and its equivalent property shows "Starter".

...which means the bean would be created using the wrong values in CasConfigurationProperties.

Research

This setup works OK using Spring Boot 2.5.6 and Spring Cloud 3.0.5. I don't think anything in Spring Boot has changed that would affect this, but I do see a number of differences in Cloud between 3.0 and 3.1.

I am not sure I can create a reproducer to adequately showcase this. I'll try. In the meantime, could you evaluate this and see if this might be seen as a bug, or misconfiguration of some kind?

0

There are 0 best solutions below