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

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:
bootstrap: contains a PropertySourceLocator for BootstrapConfiguration, defined in spring.factories file.
starter: depends on bootstrap, and it's a (servlet-based) web application
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?

Related

Upgrading from Java Config to Spring Boot

Upgrading an existing system to Spring Boot with Auto config. Currently the system is all Java config. I'm confused over whether to continue the use of #Profile. Is this annotation no longer needed? I searched extensively about upgrading and found only references to non-Spring Java migration and creating new projects.
Typical #Profile usage in our configuration classes looks something like:
#Bean
#Profile("is-standalone")
public Service unsecuredService(SomeApi someApi) {
return new ...
}
I inferred from the Spring Boot examples that using one of the #Conditional annotations is recommended like this:
#Bean
#ConditionalOnProperty("unsecured.enabled")
public Service unsecuredService(SomeApi someApi) {
return new ...
}
Then in a YAML file the is-standalone Profile enables or disables all the various properties for that Profile. Is this the proper way to upgrade? To repeat a question from above differently, can the #Profile usage be left as is? This is for a fairly large project, the upgrade is non-trivial, so I would like to do this only once!
Depends where your previous #Profile annotation is coming from. If you're using Spring's #Profile, the functionality is as follows:
Annotating a class with #Profile("dev") will load the class and register it in the Spring context only when the dev profile is active
Annotating a class with #Profile("!dev") will load the class and register it in the Spring context only when the dev profile is inactive
If this sounds like what you have already, no change is needed.

External application.properties file overriding values in Spring Boot 2.2.1

I have seen many questions in regards to Spring Boot external configuration, in specific, externalizing a application.properties file, but didn't find a specific answer on whether property values from externalized application.properties files should be merged or the whole .properties file on the classpath should be overridden with the external one.
After recent app upgrade to Spring Boot 2.2.1-RELEASE version, there was a need to enable bean overriding with spring.main.allow-bean-definition-overriding=true property, which is set in the application.properties file on the classpath. Since we want to use an external application.properties file, I created one(same name) without the aforementioned property and loaded it with the following property in IntelliJ:
java -jar appName.jar --spring.config.location="C:\Users\User\Downloads\application.properties"
The error came up as though the external props file completely overrode the classpath file, without looking for the mentioned property in the classpath file and using the missing property from the classpath application.properties:
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'userRepository' could not be registered. A bean with that name has already been defined and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
After adding the bean overriding property to the external application.properties file, but removing it from the classpath one, error is gone.
In Spring docs, it is clearly stated that :
Spring Boot uses a very particular PropertySource order that is designed to allow sensible overriding of values. Properties are considered in the following order:
...
Application properties outside of your packaged jar (application.properties and YAML variants).
Application properties packaged inside your jar (application.properties and YAML variants).
After testing it out with IntelliJ and a packaged jar, both method exhibit the same: seems like the whole file is being overridden and values are not merged, but only loaded from the externalized properties file. My questions is, is this the expected behavior or am I missing out on something ?
EDIT: tested the behavior with jar

Yaml type-safe configuration in Spring using Kotlin Functional Style

I'm trying to follow the demo https://github.com/sdeleuze/spring-kotlin-functional to create a new Spring Boot application using the annotation-free new approach, released in Spring Boot 2. My problem is how to continue to use Yaml files to configure my application, without using annotations? I would guess it would be something inside the Beans configuration but I dont find any documentation on that subject. Thanx
The beans dsl has an env property that you can use to retrieve any environment property defined in yaml, properties files or command line parameters:
fun beans() = beans {
bean<SomeBeanThatNeedsConfig> {
SomeBeanThatNeedsConfig(env.getProperty("config.value"))
}
}

Using #ConfigurationProperties in Spring Boot Application doesn't work

I am using Spring Boot V 1.4.1 for a new application.
My app requires two JDBC data sources and I was following the example at http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-two-datasources how to set it up.
My Spring beans configuration class is annotated with #EnableConfigurationProperties and my first bean is defined as
#Primary
#Bean
#ConfigurationProperties(prefix = "first.database")
DataSource qivsDB() {
return DataSourceBuilder.create().build();
}
, the second one accordingly. My application.properties file has properties defined like
first.database.url=jdbc:[redacted]
first.database.username=[redacted]
first.database.password=[redacted]
For reasons I not transparent to me during debugging this is failing to initialize: Cannot determine embedded database driver class for database type NONE - debug showed me that the builder does not have any properties set when calling build().
What did I miss here?
Before you do all the debugging part, you should have a look to the auto-configuration report. If you define your own DataSource there's no reason for Spring Boot to start looking at what it can do for your app. So, for some reasons, that definition of yours is not applied in your app and the default in Spring Boot still applies, doesn't find any JDBC url in the default namespace and attempt to start an embedded database. You should see in the auto-config report that the DataSourceAutoConfiguration still matches.
I am not sure the public keyword has anything to do with it, though you won't get custom meta-data for that key since we only scan for public methods.

Defining profile dependant YAML properties in Spring Boot 1.4

When defining properties in YAML for a Spring Boot 1.4 application, I have to use the main/resources/application.yaml file. So far, so good.
How can I override these dependencies using YAML depending for a specific profile when running or testing the application.
When using Spring Boot 1.4 (this applies for release 1.3 as well) YAML properties are always defined in the main/resources/application.yaml file. Depending on the chosen profile, the properties can be overridden by another set of properties.
To override these properties for test, a YAML file has to be given in the /test/resources/application-.yaml file, where is replaced by the active profile. It is important to see that the profile always has to be given, even when there is no active profile. In that case, the profile is ‘default’.
To override the properties when running the application, properties can be overridden for a specific profile using in a file called main/resources/application-.yaml file. Even for this situation, if no profile is given, the profile for the filename is ‘default’.
Example
The test class Spring14ApplicationTests.java for Spring Boot 1.4 has the following definition
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public class Spring14ApplicationTests {
…
}
For Spring Boot 1.3, the same file has the following definition
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes=SpringDb14Application.class)
#WebAppConfiguration
public class SpringDb14ApplicationTests {
...
}

Resources