opencodez

How to Load Application Properties from Database

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

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