Every Java application whether its simple standalone or multi aspect complex enterprise java web application it will rely on some static properties file. This property file goes through constant changes by the hands of developers, deployment and configuration managers. Everyone changes the values in properties file as per their environment. We even end up keeping multiple properties file in our project. Each file will have environment specific values for example development, staging, quality, acceptance, pre-production and production.
Once code is moved to higher environment where application is deployed through some managed process, any change in the properties file has to be checked in to scm tools and then deployed to the targeted environment. In this article we will see the utility that will load the application properties from database. Any change in the property needs only application restart and you will get updated values from database.
Software used in this example
- Java 8
- MySQL
- Eclipse
We will first define our data source as below
xbean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"x xproperty name="driverClass" value="com.mysql.jdbc.Driver" /x xproperty name="jdbcUrl" value="jdbc:mysql://localhost:3306/localdb" /x xproperty name="properties"x xpropsx xprop key="c3p0.acquire_increment"x5x/propx xprop key="c3p0.maxStatementsPerConnection"x20x/propx xprop key="c3p0.maxStatements "x100x/propx xprop key="c3p0.maxPoolSize"x500x/propx xprop key="c3p0.max_statements"x0x/propx xprop key="c3p0.minPoolSize"x5x/propx xprop key="user"xlessrootx/propx xprop key="password"x********x/propx x/propsx x/propertyx x/beanx
Once that is done, we will define our table that will hold key and value pairs for our application configuration
CREATE TABLE `localdb`.`config_params` ( `config_id` INT NOT NULL AUTO_INCREMENT, `config_key` VARCHAR(100) NULL, `config_value` VARCHAR(500) NULL, `config_description` VARCHAR(100) NULL, PRIMARY KEY (`config_id`), UNIQUE INDEX `config_key_UNIQUE` (`config_key` ASC)); --insert some sample values insert into config_params(config_key, config_value, config_description) values('con.key1', 'value1', 'description1'), ('con.key2', 'value2', 'description1');
Post this we will define our custom property processor class that will extend Springxs PropertyPlaceholderConfigurer and will load properties from database
package com.opencodez.util; import java.io.Serializable; import javax.sql.DataSource; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; public class PropertiesUtils extends PropertyPlaceholderConfigurer implements Serializable { /** The Constant serialVersionUID. */ private static final long serialVersionUID = 1L; private static CustomProperties customProps; private String dataSourceName; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { DataSource dataSource = (DataSource) beanFactory.getBean(getDataSourceName()); customProps = new CustomProperties(dataSource); setProperties(customProps); super.postProcessBeanFactory(beanFactory); } public String getDataSourceName() { return dataSourceName; } public void setDataSourceName(String dataSourceName) { this.dataSourceName = dataSourceName; } public static String getProperty(String name) { return (null == customProps.get(name)) ? "" : customProps.get(name).toString(); } }
Above class is configured as bean and it is supplied the datasource of our application. You can also note that we have set localOverride as true. This will make sure that properties from database take precedence over that available in any properties file.
xbean id="propertyConfigurer" class="com.opencodez.util.PropertiesUtils"x xproperty name="dataSourceName" value="dataSource" /x xproperty name="localOverride" value="true" /x xproperty name="locations"x xlistx xvaluexclasspath:app-custom.propertiesx/valuex x/listx x/propertyx x/beanx
And here is our custom properties class
public class CustomProperties extends Properties { private final AppLogger logger = AppLogger.getInstance(); private static final long serialVersionUID = 1L; public CustomProperties(DataSource dataSource) { super(); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); ListxMapxString, Objectxx configs = jdbcTemplate .queryForList("select config_key, config_value from config_params"); logger.info("Loading properties from Database"); for (MapxString, Objectx config : configs) { setProperty((config.get("config_key")).toString(), (config.get("config_value")).toString()); } } }
This will make sure that you will have properties loaded from database as well as any additional configuration that you can safely put in application. You can access these properties directly using @Value annotation or you can call utility method provided along with the above class. We will see now our sample properties file and its output using test controller
env=${server.env} cron.frequency.jobwithsimpletrigger=2000 cron.frequency.jobwithcrontrigger=0/7 * * * * ? con.key2 = http://abc.com
Sample Controller
package com.opencodez.controller; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.opencodez.util.AppUtil; import com.opencodez.util.PropertiesUtils; @RestController public class TestController { @Value("${con.key2}") String conKey2; @RequestMapping("/getval") public String getVal(@RequestParam(value="key", defaultValue="World") String key) { MapxString, Stringx mapOfKeyValue = new HashMapxString, Stringx(); mapOfKeyValue.put(key, PropertiesUtils.getProperty(key)); mapOfKeyValue.put("con.key2", conKey2); return AppUtil.getBeanToJsonString(mapOfKeyValue); } }
And below is the output of our rest point http://localhost:8080/getval?key=con.key1
{"con.key2":"value2","con.key1":"value1"}
We have properties value set from out database. Hope above code sample will be helpful to someone.
If you need to refer to the AppUtil class to run this application properties from database example, you can look in to one present in our Git Repo x AppUtil.java
x