How to Load Custom Spring Configuration Before CassandraDataAutoConfiguration - java

I am running a Spring Boot application connecting to a Cassandra database with Spring Data. The Spring Data connection relies on username and password that will be generated by a custom Configuration Class, but the application won't start because it loads CassandraDataAutoConfiguration before ever getting to the custom Configuration Class, causing the application to fail with an authentication error. I need to make sure that this custom Configuration class is loaded before CassandraDataAutoConfiguration.
I have tried adding #AutoConfigureBefore({CassandraDataAutoConfiguration.class}) to the custom Configuration class, but am still seeing the same issue.

If you want to configure the order in which beans are instantiated by spring you can use:
#DependsOn("A")
public class B {
...
}
This would create bean "A", than "B".
Reference

Related

Adding a conditional external PropertySource in a spring boot application

I'm currently using an EnvironmentPostProcessor to add my external PropertySource, the code looks like this :
public class ExternalPropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor
{
private static final String EXTERNAL_PROPERTY_SOURCE_NAME = "ExternalPropertySource";
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application)
{
if (environment.acceptsProfiles(Profiles.EXTERNAL_PROPERTY_SOURCE_ENABLED_PROFILE)) {
environment.getPropertySources()
.addLast(new ExternalPropertySource(EXTERNAL_PROPERTY_SOURCE_NAME, new ExternalSource()));
}
}
}
A spring.factories is also used to register this EnvironmentPostProcessor.
This code actually works if the profile is set in the vm environment variables, but if it is added in src/main/resources/application.yml, the profile doesn't seem to be injected yet in the environment and is not returned by the environment.getActiveProfiles(). I've tried using the interface Ordered with the lowest precedence, but it doesn't help.
To add a bit of context around what I'm trying to achieve, this class is in a small library that adds an external property source like a database. Then we can use it in some other spring boot applications. Something like Spring Cloud Config does.
I'd like a clean way to enable or disable this property source depending on the environment where the code runs. I don't mind using something else then profiles or even another way to inject the property source, I just want something clean that doesn't depend on several factors to work.
The main problem in my code right now is that I'm using spring boot's property sources to make my own property source configurable.
UPDATE : I used a Spring Cloud app to debug this, and was confusing the bootstrap context with the normal spring boot context. See my answer below.
Further investigation made me figure out the problem appeared only with a Spring Cloud application.
In fact the breakpoint I had in this code was triggered twice, once after the bootstrap context initialization and once after the spring boot context initialization. I was only debugging the profiles in the first occurence. At that point, only the bootstrap.yml properties are loaded and not the ones from the application.yml file. The second occurence obviously had the profiles from my application.yml file.
My code worked as expected with a vanilla Spring Boot application. As the documentation states :
The Environment has already been prepared with all the usual property
sources that Spring Boot loads by default.
I was confused by the behaviour of my app which seemed to be different from that statement, but it was Spring Cloud's bootstrap that was messing with my debugging.
Since I need a PropertySource that has the highest precedence, I need to add it in the post bootstrap initialization for Spring Cloud apps. I used an init flag on my EnvironmentPostProcessor so it doesn't get executed twice and used the bootstrap.yml on Spring Cloud apps to set the profile.
TL;DR :
With Spring Cloud, an EnvironmentPostProcessor gets called twice: once after the bootstrap init and once after the normal Spring Boot context init. If you need injected properties and are targeting the Spring Cloud's post bootstrap initialization, use the bootstrap.yml instead of application.yml.

Lazy load #EnableCaching class in spring

I want my spring boot server to be up even if my redis cache isnt up.
I am using #EnableCachingannotation , but my server startup fails if redis is down giving me BeanCreationException. Because BeanCreationException cannot be handled , the only option I have is to lazy load my #EnableCaching class.
I tried annotating that class as under:
#Configuration
#EnableCaching
#Lazy
#Profile("dev")
public class RedisCache extends CachingConfigurerSupport {}
But still this bean is getting loaded at server startup and startup therefore fails.
How do I lazy load the above mentioned class
As far as I know Spring configuration is alwasy loaded on application startup, because that is when the ApplicationContext is created.
In order to do what you want you'd either have to create some sort of custom implementation of ApplicationContext (although I honestly cannot think of how it should work regarding the dependency resolution for dependency injection) or create custom wrapper for caching which would not try to establish connection to Redis until the cache is used.
It may also be possible to configure Spring Boot to skip this particular #Configuration class (Using Boot configuration classes) and then to manually create AnnotationConfigApplicationContext and then retrieve Redis connection Beans from this context manually rather than autowiring them.

On-the-fly DB reconfiguration in Spring Boot application

I have quite a complex Spring Boot application (including Spring MVC and Security), which among other things requires a database configuration (url, username and password). The requirement is to let end-user configure the DB with the help of the application itself, like the following:
User starts the application, which has no DB configuration yet
Application notices the absence of DB configuration and presents user with configuration screen
User enters url and credentials
Application starts using this DB
The obvious problem is that I cannot create any beans that require a DataSource which requires DB configuration until that configuration is known.
Is there any way to postpone the initialisation of the majority of the application's beans until first configuration step is performed?
-- Update --
Several of application's beans initialise their state from the DB in their #PostConsutrct methods. So I either need to really delay their creation or have a method of refreshing (potentially) all beans in the application context after configuration is provided.
I suggest you could think about having another module/program or a simple script that would ask to provide all details like database etc, store this information to your application.properties file (may be useful re externalized configuration Spring boot external configuration) and then launch your main program with already available information on datasource. This might be a bit easier approach.
As you probably have expected, you need an instance of javax.sql.DataSource at the time when the Spring Application Context is created.
I suggest you subclass Spring's org.springframework.jdbc.datasource.DelegatingDataSource and override afterPropertiesSet with an empty method (the default implementation throws an error if you haven't supplied a datasource). When the user supplies the details you need to select the db, you can inject the final datasource at a later time into DelegatingDataSource.
Source: https://github.com/spring-projects/spring-framework/blob/master/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/DelegatingDataSource.java

How to update configuration of SpringBoot application at runtime without reloading whole ApplicationContext

I am trying to figure out how can I dynamically update/reload externalized configuration in a Spring Boot application without restarting the whole application.
Most of the advice involves reloading ApplicationContext after changing the externalized configuration, but that is equivalent to restarting the entire application, so this is not really all that useful.
Reading through SpringBoot reference documentation, I found a chapter 23.7 Typesafe Configuration Properties.
If I understand it correctly, this allows to define simple POJO classes that will hold your application (externalized) configuration values as attributes.
In theory at least, this scheme could be used to bind beans only once to the required configuration POJO and upon configuration change just update the values in the POJO. Components could easily pick up the changes next time they access getters on the POJO...
However, I have yet not managed to figure out how to enable this type of behavior. Is there some glaringly obvious way to dynamically update components annotated with #ConfigurationProperties when relevant configuration has changed?
It sounds like you're looking for #RefreshScope which is provided by Spring Cloud. From the Spring Cloud documentation:
A Spring #Bean that is marked as #RefreshScope will get special treatment when there is a configuration change. This addresses the problem of stateful beans that only get their configuration injected when they are initialized. For instance if a DataSource has open connections when the database URL is changed via the Environment, we probably want the holders of those connections to be able to complete what they are doing. Then the next time someone borrows a connection from the pool he gets one with the new URL.

How to re-initialize beans in spring without application server restart

I am programming service for getting data from database and providing them via REST service. It uses spring mvc. My database connection cofiguration is in property file from where it is loaded by spring as a data source bean during context initialization.
Now my problem is - I want to change configuration in properties file (for example change database info) but I can't afford to restart the application server so the new configuration does not load.
How can I re-initialize spring context or some particular beans so the newly defined properties are used?
If you want multiple data source in spring and need to deciding appropriate data source dynamically at runtime you can do this with AbstractRoutingDataSource provided with spring. You have to implement your lookup key logic for determining data-source in method determineCurrentLookupKey(). With this you can map different beans to different data-sources at runtime. following are few questions relating to this context.
How to programatically change databases in Spring with one DataSource?
Also
dynamically change Spring data source

Categories

Resources