i'm developing an spring 4.0 REST application and it works fine, but when i try to make some test it fails at reading propety values from placeholders (they work fine if i run the app normally)
Some of the properties of the app come from files and other from database so i have configured an PropertiesPropertySource to read them from db. I was unable to config it by xml, so i did it in a #Configuration class:
public class AppConfig {
private static final Logger logger = LoggerFactory.getLogger(AppConfig.class);
#Inject
private org.springframework.core.env.Environment env;
#Autowired
private DataSource dataSource;
#PostConstruct
public void initializeDatabasePropertySourceUsage() {
MutablePropertySources propertySources = ((ConfigurableEnvironment) env).getPropertySources();
try {
DatabaseConfiguration databaseConfiguration = new DatabaseConfiguration(dataSource, "[TABLE]", "property", "value");
CommonsConfigurationFactoryBean commonsConfigurationFactoryBean = new CommonsConfigurationFactoryBean(databaseConfiguration);
Properties dbProps = (Properties) commonsConfigurationFactoryBean.getObject();
PropertiesPropertySource dbPropertySource = new PropertiesPropertySource("dbPropertySource", dbProps);
propertySources.addFirst(dbPropertySource);
} catch (Exception e) {
logger.error("Error during database properties setup:"+e.getMessage(), e);
throw new RuntimeException(e);
}
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
In the test class i load the context config with this annotation:
#ContextConfiguration(classes = {TestConfig1.class,AppConfig.class,TestConfig2.class})
As i'm mixing XML and java configurations i had to create the TestConfigX classes which load the XMLs with this annotation:
#ImportResource([PATH TO config xml])
TestConfig1 has the DatasourceBean definition and it works fine
AppConfig config the PropertiesPropertySource to read the placeholders from DB
TestConfig2 config the rest of the app, which uses the placeholders ( #Value=${XXXX}), but is unable to read the values: Could not resolve placeholder XXXX
If i omit TestConfig2 i can use the placeholders from DB in the test without problem. But obviously i´m not testing anything.
What i'm doing wrong?
Related
I have a Spring Boot web service that serves different types of data, where each type resides in its own database. As new types are added, I don't want to have to configure the datasource for each type in the code. This is what I've got so far:
Data types and their database connections are defined in application.yml:
datatypes:
someType:
url: jdbc:postgresql://mydatabase.com:5432/some_type
username: user1
password: pass1
otherType:
url: jdbc:postgresql://mydatabase.com:5432/other_type
username: user2
password: pass2
A configuration class reads the properties and creates DataSources and JdbcTemplates:
#Configuration
#EnableConfigurationProperties
public class JdbcConfig {
#Bean
#ConfigurationProperties(prefix = "datatypes")
public Map<String, DataSourceProperties> databaseConfig() {
return new HashMap<>();
}
#Bean
public Map<String, NamedParameterJdbcTemplate> jdbcTemplateMap() {
Map<String, DataSourceProperties> databaseConfig = databaseConfig();
Map<String, DataSource> dataSourceMap = databaseConfig().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry ->
entry.getValue().initializeDataSourceBuilder().build()));
return dataSourceMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry ->
new NamedParameterJdbcTemplate(entry.getValue())));
}
}
And finally a repository that fetches data based on its type:
#Repository
public class MyRepository {
#Autowired
private Map<String, NamedParameterJdbcTemplate> jdbcTemplateMap;
public Item getItem(String dataType, String id) {
String sql = "select * from item where id = :id";
return jdbcTemplateMap.get(dataType)
.queryForObject(sql, Map.of("id", id), new ItemRowMapper());
}
}
When Spring tries to autowire jdbcTemplateMap in the repository it can't find any jdbc template beans, so the auto configuration kicks in but fails as the properties for the datasource are not where it expects them in the yml. This can be fixed by disabling the auto configuration: #SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
This setup almost works. However, since the DataSource instances are not registered in the application context, I miss out on some other auto configuration magic, like the actuator health check for instance. I tried registering them myself, by adding this to JdbcConfig and calling registerDataSource() from the jdbcTemplateMap() bean method:
#Autowired
private ApplicationContext applicationContext;
private void registerDataSource(String beanName, DataSource dataSource) {
ConfigurableApplicationContext context =
(ConfigurableApplicationContext) applicationContext;
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton(beanName, dataSource);
}
With this in place I should be able to enable the datasouce auto configuration again, but it runs before jdbcTemplateMap() has a chance to run. Actuator won't pick them up either. Can this be fixed?
I want to configure a tomcat data source in Spring Boot, The properties of the database are stored in another property file (Say dbConnection.properties) with different keys.
For example
dbConnection.properties:
DATABASE_URL=SomeURL
DATABASE_USER=SomeUser
DATABASE_PASSWORD=SomePassword
From what I understand the properties related to a data source must be specified in application.properties as:
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
How do I pass the values from dbConnection.properties to application.properties?
From Spring Boot documentation
Property contributions can come from additional jar files on your classpath so you should not consider this an exhaustive list. It is also perfectly legit to define your own properties.
so you can have your own property file and it should be in your classpath,
Inject property using Value annotation
#Value("#{propFileName.propKeyName}")
All you need is override the Spring-Boot's Datasource default configuration. See the example above:
#Bean
#Primary
public DataSource dataSource() {
return DataSourceBuilder
.create()
.username("") // TODO: Get from properties
.password("") // TODO: Get from properties
.url("") // TODO: Get from properties
.driverClassName("") // TODO: Get from properties
.build();
}
In order to get from properties you can use the #Value annotation that #Saravana said you.
#Manish Kothari... try this,create a configuration class with the annotations like
#ConfigurationProperties.
#Component
#PropertySource("classpath:dbConnection.properties")
#ConfigurationProperties
public class ConfigurationClass{
//some code
}
and now it will call your DB properities... I hope this will work
There are multiple ways to do this
1.You can pass the property files from the command promopt
-Dspring.config.location=classpath:job1.properties,classpath:job2.properties
2.Way is to add #PropertySource annotation
public class AppConfig
#PropertySource("classpath:config.properties")
public class LoadDbProps{
#value("${DATABASE_USER}")
private String dbUserName;
private String dbUserName;
}
Later you can set this LoadDbProps to application.properties properties using #Bean configuration.
The below solution worked for me :
#Configuration
public class DataSourceConfig {
#Bean
public DataSource getDataSource() {
Properties properties = null;
InputStream inputStream = null;
DataSourceBuilder dataSourceBuilder =null;
try {
properties = new Properties();
inputStream = new FileInputStream("./src/main/resources/config.properties");
properties.load(inputStream);
dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.url(properties.getProperty("url"));
dataSourceBuilder.username(properties.getProperty("user"));
dataSourceBuilder.password(properties.getProperty("password"));
}
catch(Exception ex) {
System.out.println("CONFIG EXCEPTION :"+ex);
}
return dataSourceBuilder.build();
}
}
refer below link for more details:
https://howtodoinjava.com/spring-boot2/datasource-configuration/#:~:text=Spring%20boot%20allows%20defining%20datasource,a%20DataSource%20bean%20for%20us.
I've the following configuration file
#Configuration
#ComponentScan(basePackages = "com.foo")
#EnableTransactionManagement
public class AppSpringConfiguration {
#Autowired
private Environment env;
#Autowired
private ApplicationContext appContext;
#Value("#{cvlExternalProperties['dbDriverClassName']}")
private String dbDriverName;
#Bean
public PropertiesFactoryBean cvlExternalProperties() {
PropertiesFactoryBean res = new PropertiesFactoryBean();
res.setFileEncoding("UTF-8");
res.setLocation(new FileSystemResource(env.resolvePlaceholders("${MY_ENV_VAR}") + "external.properties"));
return res;
}
#Bean
public BasicDataSource datasource() {
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("myDriverClassName");
basicDataSource.setUrl("MyDbUrl");
basicDataSource.setUsername("myUser");
basicDataSource.setPassword("myPass");
return basicDataSource;
}
}
And in the external properties file I've
dbUrl=jdbc:mysql://localhost:3306/someDb
dbUser=someUser
dbPassword=somePass
dbDriverClassName=com.mysql.jdbc.Driver
In which way I can use the cvlProperties inside the datasource() method?
I've tried
env.getProperty("dbDriverClassName")
env.getProperty("#cvlProperties['dbDriverClassName']")
But I'm not able to retrieve the properties.
The field dbDriverName is correctly filled, so that means the bean declaration is ok.
I want to use the PropertyFactoryBean class because in this way I can specify the encoding to use.
If I use the the following annotation on the top of the configuration class
#PropertySource("file:${MY_ENV_VAR}/external.properties")
I'm able to retrieve the properties with this piece of code
env.getProperty("dbDriverClassName")
But the encoding used by the PropertySource annotation is the windows default, and for me is not correct.
Can you help me?
At the moment the solution(that I don't love so much) is to declare the properties by using the annotation #Value
#Value("#{cvlExternalProperties['dbDriverClassName']}")
private String dbDriverClassName;
and then using it inside the java class
I have an spring-boot application with 3 #Configuration annotated classes: Application, DBScannerConfig and ElasticSearchConfiguration. My application also consumes configuration from a Spring Cloud Config Server.
The problem is that, when I try to annotate attributes with #Value("${key}") inside the DBScannerConfig class, the properties don't get injected. However, if I use the #Value("${key}") annotation in the other two classes, the configuration works well.
I actually think it could be related to the #MapperScan annotation present in my DBScannerConfig configuration class.
What I would like to do is to either be able to retrieve properties or autowire spring's datasource in my #MapperScan annotated class, but I'm not being able to achieve such thing.
Does anyone faced this problem before? Here's the code:
Application.java
#SpringBootApplication
#EnableElasticsearchRepositories("org.myapp.elastic.repository")
#ComponentScan(basePackages = {"org.myapp"})
public class Application extends WebSecurityConfigurerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
// #Autowired
// DataSource dataSource;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
//Other Stuff for Spring Security
}
DBScannerConfig.java
#Configuration
#MapperScan("org.myapp.persistence.mapper")
public class DBScannerConfig{
#Autowired
DataSource dataSource;
#Bean(name = "mySqlSessionFactory")
public SqlSessionFactory mySqlSessionFactory() {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setTypeAliasesPackage("org.myapp.persistence.entity");
SqlSessionFactory sqlSessionFactory = null;
try {
sqlSessionFactory = sessionFactory.getObject();
} catch (Exception e) {
LOGGER.error("Error creating mySqlSessionFactory", e);
}
return sqlSessionFactory;
}
}
If I uncomment the #Autowired datasource in Application.java I get a valid object. However, in the DBScannerConfig.java what I get is NULL.
I tried to autowire the Datasource object inside my Application.java class and then use it in the DBSCannerConfig.java with no luck. As soon as I add the #MapperScan annotation into the Application.java class it stops autowiring the spring datasource.
I need to get the following system property within one of my configuration files: -Dspring.profiles.active="development". Now I have seen countless of people suggest that this can be done with Spring Expression Language, but I cannot get it to work. Here is what I have tried (plus many variations hereof).
#Configuration
#ComponentScan(basePackages = { ... })
public class AppConfig {
#Autowired
private Environment environment;
#Value("${spring.profiles.active}")
private String activeProfileOne;
#Value("#{systemProperties['spring.profiles.active']}")
private String activeProfileTwo;
#Bean
public PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
Resource[] resources = {
new ClassPathResource("application.properties"),
new ClassPathResource("database.properties")
// I want to use the active profile in the above file names
};
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setLocations(resources);
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(true);
return propertySourcesPlaceholderConfigurer;
}
}
All of the properties are NULL. The same happens if I try to access any other system property. I can access them without problems by invoking System.getProperty("spring.profiles.active"), but this is not as nice.
Many examples that I found configure the PropertySourcesPlaceholderConfigurer to also search system properties, so maybe this is why it does not work. However, those examples are in Spring 3 and XML configuration, setting a property that no longer exists on the class. Perhaps I have to call the setPropertySources method, but I am not entirely sure how to configure this.
Either way, I found multiple examples that suggest that my approach should work. Believe me, I searched around a lot. What's wrong?
Just autowire an instance of Spring's Environment interface, as you're actually doing and then ask what the active environment is:
#Configuration
public class AppConfig {
#Autowired
private Environment environment;
#Bean
public PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
List<String> envs = Arrays.asList(this.environment.getActiveProfiles());
if (envs.contains("DEV")) {
// return dev properties
} else if (envs.contains("PROD")) {
// return prod properties
}
// return default properties
}
}