I am loading settings from a YAML file and having Spring autowire the results in a Properties bean like so:
#ConfigurationProperties(prefix = "myPrefix")
#Bean
private Properties getProperties() {
return new Properties();
}
However, the Properties class is rather limiting, and I would like to have an Apache commons config Configuration object. The commons config documentation says that it can be integrated with Spring, but I don't see an example for this simple use case.
How can I autowire an apache commons Configuration in Spring Boot?
I do not think there is any ready-made solution for getting an Apache Commons Configuration object. However, you can get Spring's Environment object, which implements PropertyResolver, which is much more advanced than Properties (you can retrieve properties of any class type). You can get it autowired in your application's constructor like so:
...
private final Environment env;
#Autowired
public MyApplication(Environment env) {
this.environment = env;
}
...
Related
I have a service which is which consumes many external services. I am creating properties file for each of them, these are quite few predefined properties such as twil.props, tweet.props, hubpot.props etc
So as to get those properites at runtime I am using PropertiesLoaderUtils like below:
Resource resource = new ClassPathResource("/"+apiname +".properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
I would like to get these properties into POJO just like ConfigurationProperties, I have designed following POJO for that purpose:
public class APIConfig {
private Integer paginationPerPage;
private String paginationKeyword;
private String paginationStyle;
private String countParamKeyword;
private String countKey;
private String offsetKey;
}
I will maintain properties file in such a way so that these can be easily mapped to Config POJO:
Properties for twil.properties
api.paginationPerPage=10
api.paginationKeyword=limit
api.paginationStyle=offset
api.countParamKeyword=count
api.countKey=count
api.offsetKey=offset
So can I get this directly into given POJO by utilizing any of Spring Boot / Spring utility, config etc?
As it is noticed at comments, the only right solution here which includes zero downtime, namely #RefreshScope.
Use spring cloud config dependency
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
Add to your code
#Configuration
class SomeConfiguration {
#Bean
#RefreshScope
#ConfigurationProperties("twil.props")
APIConfig twilConfig() {
return new ApiConfig()
}
#Bean
#RefreshScope
#ConfigurationProperties("tweet.props")
APIConfig tweetConfig() {
return new ApiConfig()
}
}
Usage: #Autowire APIConfig tweetConfig; to any bean
Call /refresh endpoint to refresh beans by new values from property sources
Please make a solution uniform with Spring ecosystem.
If you want to have dynamically to be dependent on, for example, #PathVariable:
private Map<String, AppConfig> sourceToConfig;
#GetMapping("/{source}/foo")
public void baz(#PathVariable source) {
AppConfig revelantConfig = sourceToConfig.get(source);
...
}
Spring provides the opportunity to automatically collect a map of beans where bean key is a bean name.
Rename bean methods above from twilConfig to twil() and call you endpoint like: /twil/foo (twilConfig is a bad path for an endpoint)
I have a simple question about PropertySourcesPlaceholderConfigurer and #PropertySource annotation. I have simple class with bean. I would like to inject property from application.properties and some another test.properties wiht #Value annotation. I read in several sources that #PropertySource needs to define staticPropertySourcesPlaceholderConfigurer bean. From documentation:
In order to resolve ${...} placeholders in 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.
But for me it works fine without this bean. Is Spring in some way auto configures PropertySourcesPlaceholderConfigurer at application startup? Here is my simple example (first property is from application.properties and second from test.properties):
#Configuration
#PropertySource("classpath:test.properties")
public class AppConfiguration {
#Value("${first.property}")
private String firstProp;
#Value("${second.property}")
private String secondProp;
#Bean
public TestModel getModel() {
TestModel model = new TestModel();
model.setFirstProperty(firstProp);
model.setSecondProperty(secondProp);
return model;
}
}
The static PropertySourcesPlaceholderConfigurer bean is registered automatically when missing by PropertyPlaceholderAutoConfiguration class which appears to be introduced in Spring Boot 1.5.0.RELEASE (as there is no on-line javadoc for it in the previous releases).
Interestingly this new auto configuration is not mentioned in Spring Boot 1.5 Release Notes.
I have 2 (or more) different configuration properties file located in the project and I want to load them for different datasources.
I tried to do as following:
#Bean
#ConfigurationProperties(locations = {"#{myconfigroot.getRootFolder()}/datasource1.properties"}
public static DataSource getFirstDatasource() {
return DataSourceBuilder.create().build();
}
But obviously this won't work as the ConfigurationProperties annotation locations property doesn't go through the spEL. (Or may be I write it wrong?) myconfigroot.getRootFolder() is a static method which returns the path to the datasource1.properties.
Please advice. Thanks.
===== Edited =======
I believe this is a common problem when somebody want their application want to load different configuration files. Due to some reasons the file location and name can't be put in the startup script or command line, or, the path can only be determined in runtime, that would require spring to load them during the bean creation.
I tried once using PropertySourcePlaceHolderConfigurer but seems not work either.
Anybody can share some lights?
Latest Spring boot (version 1.3.5) doesn’t support SpEL in this case.
See JavaDoc of annotation #ConfigurationProperties
Note that contrary to {#code #Value}, SpEL expressions are not
evaluated since property values are externalized.
I found a way to customize Spring boot default behavior as follows:
For example, I have database.properties file in somewhere, for some reason I cannot get the location before runtime.
username=mike
password=password
Accordingly, define POJO mapping to properties:
#Component
#ConfigurationProperties(locations = "myConfiguration")// myConfiguration is customized placeholder
public class MyProperties{
String username;
String password;
//Getters, Setters…
}
Then, to extend default StandardEnvironment:
public class MyEnvironment extends StandardEnvironment {
#Override
public String resolvePlaceholders(String location) {
if (location.equals("myConfiguration")) {
//Whatever you can do, SpEL, method call...
//Return database.properties path at runtime in this case
return getRootFolder() + "datasource.properties";
} else {
return super.resolvePlaceholders(text);
}
}
}
Last, apply it in Spring boot main method entry:
#SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
new SpeedRestApplication()
.configure(new SpringApplicationBuilder(SpeedRestApplication.class).environment(new MyEnvironment()))//Replace default StandardEnvironment
.run(args);
}
}
Once Spring boot starts up, the MyProperties bean name and password fields are injected from database.properties. Then you could wire the MyProperties bean to other beans as configuration.
Hope it helps!
I finally got it work by using the following mechanism:
public class DatasourcePostProcessor implements EnvironmentPostProcessor {
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Properties p = new Properties();
p.load(new FileInputStream(new File(getRootFolder() + "/datasource1.properties")));
Map<String, Object> propMap = new HashMap<>();
for (Map.Entry<Object, Object> entry : p.entrySet()) {
propMap.put(entry.getKey().toString(), entry.getValue());
}
MapPropertySource source = new MapPropertySource("datasource1", propMap);
environment.getPropertySources().addLast(source);
}
}
and register the environment post processor into the spring.factories:
org.springframework.boot.env.EnvironmentPostProcessor=com.myorg.test.DatasourcePostProcessor
Anyway, hope this helps people and accept the first anwer as it enlight me. Also post the following references from the google search that found during research:
Where I found how to wire the property source with the environment: https://github.com/spring-projects/spring-boot/issues/4595
Where I found how to load the customized properties file: How to configure a custom source to feed Spring Boot's #ConfigurationProperties
I am trying to convert a XML based configuration to JAVA based configuration. Can someone please let me know the java annotation based configuration for the following
<jms:outbound-channel-adapter channel="requestChannel"
connection-factory="testConnectionFactory"
destination-name="${jms.queueName}"
message-converter="messageConverter"/>
I tried having a look at this Reference doc. But i am not able to understand how do I map the above xml to the annotation config.
#ServiceActivator(inputChannel="requestChannel")
#Bean
public MessageHandler outbound(JmsTemplate jmsTemplate) {
JmsSendingMessageHandler handler = new JmsSendingMessageHandler(jmsTemplate);
handler.setDestinationName(...);
...
return handler;
}
#Bean
public JmsTemplate jmsTemplate(ConnectionFactory jmsConnectionFactory) {
...
template.setMessageConverter(converter());
return template;
}
Then add the connection factory and converter beans.
EDIT
Also pay attention to Spring Integration Java DSL project, which provides the org.springframework.integration.dsl.jms.Jms Factory on the matter. You can find its usage in the JmsTests: https://github.com/spring-projects/spring-integration-java-dsl/blob/master/src/test/java/org/springframework/integration/dsl/test/jms/JmsTests.java
When my SpringApplicationContext is being boostrapped together, I want to get some values for that out of a properties file. From what I've read, it's best to use PropertySourcesPlaceholderConfigurer for this. I'm creating that bean in my spring java config and it is properly resolving the properties.
The problem is when I'm trying to use those properties in the construction of my other spring beans, like in my hibernate config. In my hibernate config, I have the following field:
#Value(value = "${myapp.database-access.url}")
public static String DB_ACCESS_URL;
Then I use that property to declare the url to access the database in my datasource bean like this:
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(DRIVER_CLASS_NAME);
dataSource.setUrl(DATABASE_URL);
dataSource.setUsername(DATABASE_USERNAME);
dataSource.setPassword(DATABASE_PASSWORD);
return dataSource;
}
Unfortunately the value is null at that point so my application bootstrapping fails. Post-construction the value is available, but apparently not mid-construction. Is there a way to get the #Value annotation to work for me in this circumstance, or do I have to use something else?
If I have to use something else, does anyone have any suggestions?
Spring does not allow injecting to static fields.
One approach:
public class Config {
public static String DB_ACCESS_URL;
#Value(value = "${myapp.database-access.url}")
public void setDbAccessUrl(String dbAccessUrl) {
Config.DB_ACCESS_URL = dbAccessUrl;
}
}
I think this is similar to the issue I was facing with the #PropertySource annotation where #Value wasn't injected in #Configuration POJOs. There are 2 ways I found to solve this:
1) Create a Spring XML config (if you don't have one already) which you'll refer to at your #Configuration class using #ImportResource and have in there a single entry: <context:property-placeholder />. That config should make #Value injection on the #Configuration class to work.
2) Don't use #Value in the #Configuration POJO but instead inject Environment and retrieve the props from there:
#Autowired Environment env;
//and further down at your dataSource bean retrieve the properties
//...
env.getProperty("myapp.database-access.url");
This is late but you can simply use #Value with constructor injection public DataSource dataSource(#Value(value = "${myapp.database-access.url}"))