I have a main Micronaut service. I also have a library project which houses some beans. One such bean (reduced) is:
#Singleton
public class Testo {
#Value("${testo}")
public int testo;
}
I am successfully "importing" this bean and #Injecting it as described in the documentation. However, I cannot for the life of me set this "testo" property from the main service. System properties, Environment variables, PropertyResource files (application.yml), at any level -> nothing works.
I check this through both a printout of the variable in the terminal, or debugging using IDEA.
Is it possible to set properties of beans that are housed in a library project?
Micronaut v3.1.3
Java 11
Is it possible to set properties of beans that are housed in a library
project?
It is. If you have #Value("${some.config.value}") on a property in a bean that is defined in your application or a bean that is defined in a properly compiled Micronaut bean provided by a library, then you can provide a value for some.config.value through any of the supported config source mechanism and that value will be used to initialize the property marked with #Value.
Of course that only works if the bean is being managed by the Micronaut context. If you are doing something like new Testo() then the #Value annotation will have no effect.
Related
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.
I want to add a custom property to the application.yml of my Cloud Config. The comments in the file say it is for all shared configuration. However, when I do so, it causes the binding of the properties to JHipsters own ApplicationProperties to fail at the class does not have the correct writable property.
application.yml
application:
clients:
- Foo
Stacktrace:
Caused by: org.springframework.boot.bind.RelaxedBindingNotWritablePropertyException: Failed to bind 'application.clients[0]' from 'file:central-config/localhost-config/application.yml' to 'clients[0]' property on 'io.github.jhipster.registry.config.ApplicationProperties'
From JHipster's documentation:
Application-specific properties Your generated application can also
have its own Spring Boot properties. This is highly recommended, as it
allows type-safe configuration of the application, as well as
auto-completion and documentation within an IDE.
JHipster has generated a ApplicationProperties class in the config
package, which is already preconfigured, and it is already documented
at the bottom the application.yml, application-dev.yml and
application-prod.yml files. All you need to do is code your own
specific properties. (emphasis mine)
Have you done that step and added your own properties to ApplicationProperties.java? It looks like you should have a property of type List<String> clients. If you haven't that's why it's failing, because it's attempting to bind a configuration property to the ApplicationProperties class, but the class doesn't contain a property to store it.
Custom Spring boot properties should always be defined in a #ConfigurationProperties class within the app so that their value can be setup in the yml file. This is thoroughly documented in the spring boot documentation:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
I'm building multiple Spring Boot applications which will be deployed on the same servlet container. But I'm having difficulties getting Spring Boot to work with externalized configuration files the way I want, and not like the framework wants.
Situation:
multiple Spring Boot applications will be deployed on a single servlet container (WAR file)
the location of configuration files will be set via JVM property spring.config.location
embedded deployment is not an option
Problem:
As the applications are deployed on the same JVM, the property spring.config.location has the same value for all applications. I want our applications to all use the same configuration file naming (application.properties), so specifying spring.config.name is not an option.
What I would like:
no need to set spring.config.name as the configuration name should be standardized accross all our applications (constant)
the externalized configuration properties should override values from application.properties packaged inside my deployed WAR
profile specific configurations (application-{profile}) should be possible
no hardcoded config locations in code
organizing configuration files in a per-application directory layout:
${spring.config.location}/app1/application.properties
${spring.config.location}/app2/application.properties
${spring.config.location}/app3/application.properties
Questions:
Is there some mechanism to influence or override loading or resolving of external configuration files?
Are there other approaches to get the desired result?
You can achieve what you're trying by using #PropertySource. According to the official documentation (Externalized Configuration) you can use this annotation to externalize configuration files, e.g.:
#Configuration
#PropertySource("file:/path/to/application.properties")
public class AppConfig {
}
As stated in here, inside #PropertySource you can use placeholders that would be resolved against other property sources, for e.g. values declared in application.properties
Assuming that "my.placeholder" is present in one of the property sources already registered, e.g. system properties or environment variables, the placeholder will be resolved to the corresponding value. If not, then "default/path" will be used as a default. Expressing a default value (delimited by colon ":") is optional. If no default is specified and a property cannot be resolved, an IllegalArgumentException will be thrown.
You can declare properties_home as an environment variable and application_id inside your application.properties file.
#Configuration
#PropertySource("${properties_home}/${application_id}/application.properties")
public class AppConfig {
}
Don't forget to enable support for resolving the placeholders:
In order to resolve ${...} placeholders in bean definitions or #Value annotations using properties from a PropertySource, one must register a PropertySourcesPlaceholderConfigurer. This happens automatically when using in XML, but must be explicitly registered using a static #Bean method when using #Configuration classes.
Update:
In order to override the properties from the external file you could use a spring profile. Inside the packaged application.properties you need to set:
spring.profiles.active=external
Declare all the properties that you want to take precedence as part of the external profile inside the file located in "${properties_home}/${application_id}/application.properties".
The suggestions of #Iulian Rosca to use a pattern like ${properties_home}/${application_id}/application.properties brought me to the idea of defining a custom JVM property like app.config.root and using this property to override spring.config.location very early in the application lifecycle.
My application class looks now like this and works for embedded and container deployments:
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return configureApplication(builder);
}
public static void main(String[] args) {
configureApplication(new SpringApplicationBuilder()).run(args);
}
private static SpringApplicationBuilder configureApplication(SpringApplicationBuilder builder) {
return builder
.sources(Application.class)
.properties("spring.config.location:${${app.config.root}/myapp1/:#{null}}");
}
}
Important notes to this solution:
app.config.root has to be set externally by JVM or JNDI property
app.config.root can only contain a single external configuration path (for my requirements this was sufficient) in contrast to spring.config.location where multiple comma-separated paths can be specified
SpringApplicationBuilder.properties(...) sets the application's default properties. Because of this, spring.config.location cannot be specified externally anymore, as JVM or JNDI Properties have priority over default properties and would therefore override spring.config.location again.
I have a Spring context xml file and a specific bean into it that created by a PropertyPlaceholderConfigurer mechanism.
My problem is: When the application is working related properties file changes and I don't want restart application. I want reload related bean without restart application.
What is best solution for my goal?
you can have a look at ReloadablePropertiesAnnotation on github https://github.com/jamesemorgan/ReloadablePropertiesAnnotation
this should just work by annotating your property like
#ReloadableProperty("dynamicProperty.myProperty")
private String myProperty;
you'll have to add some spring configuration as well see the example on github or in this blog http://www.morgan-design.com/2012/08/reloadable-application-properties-with.html
Under the hood, this is using Guava's EventBus to update the properties of your beans after the bean has been created.
Is there a way to update a Spring bean dynamically if the spring beans configuration changes?
E.g. assume that I have a spring bean with boolean property x and the spring beans has the value true when the application starts.
So spring creates the bean with the property x set to true.
Is there a way so that if I changes the property to x (while the application is running) that the property will be updated e.g. to false?
Calling the setter for x setX() method will do that.
But it should not be a prototype bean.
it is possible with the jrebel-spring integration. it monitors your configuration and TRIES to re-wire your beans at runtime.
Though i would not use it in production...only for playing around, testing etc
Spring reads configuration files at startup. If you really need to update configs while application running, you should manually implement all the chain: detecting config changes, validating config, detecting changed beans, updating beans in context.
Spring beans can be initialized using applicationContext.xml or even programatically. In your case; you will need to remove configurations from xml and add into java program. You can get some idea from How to programmatically create bean definition with injected properties? . Other good links were also available on google.