To summarize: the problem is that we want to set the ContextSource properties using values retrieved from a database rather than from a properties file. The context configuration file looks something like this:
I skipped the ContextSourceFactoryBean approach and went back to look at the post about using Apache Commons Configuration from my colleague Alin Dreghiciu. I got it working. At the moment, the database must be running before the context is loaded. We will assume it contains this information:Code:<beans ...> <bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource"> <property name="url" value="${ldap.url}" /> <property name="base" value="${ldap.base}" /> <property name="userDn" value="${ldap.userDn}" /> <property name="password" value="${ldap.password}" /> </bean> <bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate"> <constructor-arg ref="contextSource" /> </bean> </beans>
Table and column names are all configurable. The db.properties file contains this:Code:CREATE TABLE myconfig ( key VARCHAR NOT NULL PRIMARY KEY, value VARCHAR ); INSERT INTO myconfig (key, value) VALUES ('ldap.url', 'ldap://localhost:3900'); INSERT INTO myconfig (key, value) VALUES ('ldap.base', 'dc=jayway,dc=se'); INSERT INTO myconfig (key, value) VALUES ('ldap.userDn', 'uid=admin,ou=system'); INSERT INTO myconfig (key, value) VALUES ('ldap.password', 'secret');
The key points in this solution are:Code:jdbc.driver=org.hsqldb.jdbcDriver jdbc.url=jdbc:hsqldb:hsql://localhost/aname jdbc.username=sa jdbc.password= config.table=myconfig config.keyColumn=key config.valueColumn=value
- the propertiesArray property of PropertyPlaceholderConfigurer (Spring)
- the DatabaseConfiguration (Commons Configuration)
- the MethodInvokingFactoryBean (Spring)
The propertiesArray gives us the ability to merge properties from several sources; in our case a file (for the DataSource properties) and a database (for the ContextSource properties).
The DatabaseConfiguration performs all the work necessary to read properties from a database table. The internal configuration format is converted into standard Properties by the ConfigurationConverter:Code:<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="propertiesArray"> <list> <ref bean="myPropsFromFile" /> <ref bean="myPropsFromDb" /> </list> </property> </bean> <bean id="myPropsFromFile" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="location" value="classpath:/conf/db.properties" /> </bean>
The MethodInvokingFactoryBean is used to wire up String beans with property values retrieved from the file configuration source:Code:<bean id="myPropsFromDb" class="org.apache.commons.configuration.ConfigurationConverter" factory-method="getProperties"> <constructor-arg> <bean class="org.apache.commons.configuration.DatabaseConfiguration"> <constructor-arg ref="dataSource" /> <constructor-arg ref="prop.config.table" /> <constructor-arg ref="prop.config.keyColumn" /> <constructor-arg ref="prop.config.valueColumn" /> </bean> </constructor-arg> </bean>
These values are used in the DatabaseConfiguration bean, as well as in the DataSource bean.Code:<bean id="prop.jdbc.driver" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetObject" ref="myPropsFromFile" /> <property name="targetMethod" value="getProperty" /> <property name="arguments"> <list> <value>jdbc.driver</value> </list> </property> </bean> ...
Note that we're using normal bean dependencies and no custom Java code. Spring will make sure that the sequence is correct. It goes like this:Code:<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" ref="prop.jdbc.driver" /> <property name="url" ref="prop.jdbc.url" /> <property name="username" ref="prop.jdbc.username" /> <property name="password" ref="prop.jdbc.password" /> </bean>
- The PropertyPlaceholderConfigurer is a BeanFactoryPostProcessor, so before any beans are created, it will be called on to replace property placeholders from its set of properties. However, in order to be instantiated, it does need a few beans.
- First of all, the configurer needs the myPropsFromFile bean, which will be created and load the db.properties (containing JDBC driver, url and such).
- Having the myPropsFromFile bean allows the MethodInvokingFactoryBeans to get the database properties mentioned above.
- This in turn allows the DataSource and the DatabaseConfiguration to be created.
- The myPropsFromDb bean is created using a somewhat advanced version of the factory-method concept. The bean is actually the result of a call to the static method ConfigurationConverter.getProperties that takes the DatabaseConfiguration bean as argument and returns a Properties object containing all the properties from the database. Confusing? Read more here and here.
- The PropertyPlaceholderConfigurer does now have all its required beans. It will go through the context and replace all ${} placeholders with the corresponding property value.


Reply With Quote
