Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring : Loading Properties from Database and local files

Tags:

spring

I am trying to load properties from database. I got successful in that. But now problem is that, for dataSource bean, i want to use placeholders. Please see applicationProperties.xml file, then one can get idea:

<!-- Data Source Bean -->
<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${driverClassName}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
</bean>

<!-- My Own class for managing properites came from Database -->
<bean class="PropFromDB.PropFromDB.PropertiesUtil" >
    <property name="propertiesArray">
        <list>
            <ref bean="propertiesFromDB" />
        </list>
    </property>
</bean>

<!-- PropertiesFactoryBean bean -->
<bean id="propertiesFromDB"
    class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="properties" ref="commonsConfigurationFactoryBean" />
</bean>

<!-- CommonsConfigurationFactoryBean bean -->
<bean id="commonsConfigurationFactoryBean"
        class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
    <constructor-arg ref="databaseConfiguration"></constructor-arg>
</bean>

<!-- DatabaseConfiguration bean -->
<bean name="databaseConfiguration" 
    class="org.apache.commons.configuration.DatabaseConfiguration">
    <constructor-arg index="0" ref="dataSource" />
    <constructor-arg index="1" value="properties" />
    <constructor-arg index="2" value="key" />
    <constructor-arg index="3" value="value" />
</bean>

The above code is for loading properties from DB. now as you can see for dataSource bean, there are some placeholders used. So i included this line at top:

<context:property-placeholder location="classpath:databaseForConfiguration.properties"/>

databaseForConfiguration.properties contains all required properties and is in classpath :

driverClassName=org.postgresql.Driver
url=jdbc:postgresql://localhost:5432/mydb
username=user
password=pass

But when i try to execute, i get following exception:

 Caused by: org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'driverClassName' threw exception; nested exception is java.lang.IllegalStateException: Could not load JDBC driver class [${driverClassName}]
    at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:108)
    at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:62)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1489)
    ... 60 more

and similar for {url}, {username},{password}.

As it's simple to understand, that for PropertiesUtil bean to initialize, first dataSource bean need to initialize. And for dataSource bean, local properties placeholders must be there. Which in this case, not getting.

I want both these things, load placeholders from local files and also from database.

Anyone please help me to resolve this problem.

thanks in advance.

like image 365
Jaydeep Avatar asked Jun 09 '14 06:06

Jaydeep


People also ask

Where do you put properties in spring boot project?

Spring Boot Framework comes with a built-in mechanism for application configuration using a file called application. properties. It is located inside the src/main/resources folder, as shown in the following figure.


2 Answers

For

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${driverClassName}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
</bean>

I created factory for datasource. and now it's look like this:

<bean id="dataSource" class="core.factory.DataSourceFactory" factory-method="createDataSource">
        <constructor-arg type="java.lang.String" value="DBConfig.properties" />
     </bean>

The DataSourceFactory class look like this:

public class DataSourceFactory
{

    private static final String DRIVER_CLASS_NAME = "db.driver";
    private static final String URL = "db.url";
    private static final String URL_LOGDB = "logdb.url";
    private static final String USER_NAME = "db.username";
    private static final String PASSWORD = "db.password";

    public static DataSource createDataSource(String propertyFilename) throws IOException
    {
        Properties properties = getProperties(propertyFilename);
        return getDataSource(URL, properties);
    }

    public static DataSource createDataSourceForLogDb(String propertyFilename) throws Exception
    {
        Properties properties = getProperties(propertyFilename);
        return getDataSource(URL_LOGDB, properties);
    }

    private static Properties getProperties(final String fileName) throws IOException
    {
        Properties properties = new Properties();
        InputStream in = DataSourceFactory.class.getClassLoader().getResourceAsStream(fileName);
        properties.load(in);
        in.close();
        return properties;
    }

    private static DataSource getDataSource(final String url, final Properties properties)
    {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(properties.getProperty(DRIVER_CLASS_NAME));
        dataSource.setUrl(properties.getProperty(url));
        dataSource.setUsername(properties.getProperty(USER_NAME));
        dataSource.setPassword(properties.getProperty(PASSWORD));
        return dataSource;
    }
}

Now i am able to get properties from local file as well as database, too.

Regarding another problem i put in comments, which was to load i18n resources from database,i got solution from this link: Spring MVC: Database MessageSource fall back to properties file.

I myself posted question and now i got solution. Hope this will be useful for someone.

like image 157
Jaydeep Avatar answered Nov 15 '22 15:11

Jaydeep


First of all you missunderstood <context:property-placeholder>. Its location is for file, not Properties object.

Right, your databaseForConfiguration.properties contains a Properties object from DB, but it isn't a .properties file.

So, try this:

<bean id="myProperties" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
      p:staticMethod="org.apache.commons.configuration.ConfigurationConverter.getProperties"
      p:arguments-ref="databaseConfiguration"/>

<context:property-placeholder properties-ref="myProperties"/>

From other side it isn't clear how you are going to load properties from DB, if you try to use placeholders for DataSource options. In this case you already should already have a configured <context:property-placeholder>...

UPDATE

There is reason to mix several answers from different topics. Asn I've shown above the MethodInvokingFactoryBean does the stuff to build Properties object.

If your <context:property-placeholder> can't resolve placeholders (Could not load JDBC driver class [${driverClassName}]), then it doesn't see your databaseForConfiguration.properties in the classpath.

Be sure to use correct location value, e.g. location="classpath:WEB-INF/databaseForConfiguration.properties", if your file is there with WAR.

like image 37
Artem Bilan Avatar answered Nov 15 '22 17:11

Artem Bilan