Does Spring support nested properties across multiple properties files? - spring

I'd like to separate properties across multiple files and allow files loaded later to have properties that refer or combine properties defined previously (nested). For example:
application.properties (e.g. dbName=test, dbHost=targethost)
mysql.properties (e.g. jdbcDriver=com.mysql.jdbc.Driver, jdbcUrl=jdbc:mysql://${dbHost:localhost):${dbPort:3306}/${dbName})
And I use declarations such as:
<context:property-placeholder location="classpath:application.properties" order="0" ignore-unresolvable="false"/>
<context:property-placeholder location="classpath*:${dbType:mysql}.properties" order="1" ignore-unresolvable="false"/>
<bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
<property name="driverClass" value="${jdbcDriver}"/>
When I try to create a dataSource in Spring config I get an error saying that ${dbName} or ${jdbcDriver} is unresolved.
For example:
Could not resolve placeholder 'jdbcDriver' in string value "${jdbcDriver}";
Could someone shed light if it is possible to organize properties into different files in this fashion, and if so, how to configure Spring to resolve the properties properly so my bean declaration does not fail?
Thanks in advance.

Related

Reference grailsApplication in resources.xml or other xml

Is there any way to reference grailsApplication from either resources.xml or a different xml file that is imported by resources.groovy?
For example, I am wiring up some beans in foo.xml that will be imported in resources.groovy. I would like to pull in some values from the external config, so it would be nice if I could do something like:
<bean id=fooBean class="com.beans.foo.FooBean">
<property name="exVar" value="${grailsApplication.config.some.path.to.ext.variable}"
</bean>
I've done this quite a bit in resources.groovy, so was wondering if there were a way to get at this object from xml. Thanks!
Looking at this related question you can use:
<bean id=fooBean class="com.beans.foo.FooBean">
<property name="exVar" value="${some.path.to.ext.variable}"
</bean>

context:property-placeholder doesn't resolve references

I have next applicationContext.xml file on the root of classpath:
<context:annotation-config />
<context:property-placeholder location="classpath:props/datasource.properties" />
<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource"
p:username="${jdbc.username}"
p:password="${jdbc.password}"
p:url="${jdbc.url}"
p:driverClassName="${jdbc.driverclass}"
p:validationQuery="SELECT sysdate FROM dual" />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
p:dataSource-ref="datasource"
p:mapperLocations="classpath:mappers/*-mapper.xml" />
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="datasource" />
<bean id="mappeScannerConfigurere" class="org.mybatis.spring.mapper.MapperScannerConfigurer"
p:sqlSessionFactory-ref="sqlSessionFactory"
p:basePackage="com.mypackage" />
props/datasource.properties also exists on the root of classpath with such content:
jdbc.url=myjdbcurl
jdbc.driverclass=myClass
jdbc.username=myUserName
jdbc.password=myPassword
I have a spring managed test where I declare to use previously mentioned applicationContext.xml via next annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:applicationContext.xml"})
When I invoke test method i get next error from spring:
org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class '${jdbc.driverclass}'
As I understand sping didn't resolve reference to jdbc.driverclass.
What have I done wrong?
PS: I'm using spring 3.2.3.RELEASE
**
EDIT
**
Perhaps the problem may be in MapperScannerConfigurer. It is a BeanDefinitionRegistryPostProcessor and as Javadoc says:
Extension to the standard BeanFactoryPostProcessor SPI,
allowing for the registration of further bean definitions
before regular BeanFactoryPostProcessor detection kicks in
So MapperScannerConfigurer instantiates datasource object via sqlSessionFactory with BeanFacoryPostProcessor(which is responsible for <context:property-placeholder/>) have not been utilized.
So my question transforms to how to reorder BeanFacoryPostProcessor from <context:property-placeholder/> and BeanDefinitionRegistryPostProcessor(MapperScannerConfigurer)?
Resolved
After a couple hours of investigation I found the solution:
As I said earlier MapperScannerConfigurer is a BeanDefinitionRegistryPostProcessor which fires before BeanFactoryPostProcessor which is responsible for <context:property-placeholder/>. So, during the creation of MapperScannerConfigurer references to external properties will not be resolved. In this case we have to defer the creation of datasource to the time after BeanFactoryPostProcessorhave been applied. We can do that in several ways:
remove p:sqlSessionFactory-ref="sqlSessionFactory" from MapperScannerConfigurer. In this case datasource object will not be created before MapperScannerConfigurer, but after BeanFactoryPostProcessor which is responsible for <context:property-placeholder/>. If you have more than one sqlSessionFactory in applicationContext, than can be some troubles
In versions of mybatis-spring module higher than 1.0.2 there is a possibility to set sqlSessionFactoryBeanName instead of sqlSessionFactory. It helps to resolve PropertyPlaceHolder issue with BeanFactoryPostProcessor. It is a recommended way to solve this issue described in mybatis-spring doc
I was having the same issue and came across this post but I was unable to resolve it the same way maks did. What ended up working for me was to set the ignoreUnresolvablePlaceholders property value to true.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:database.properties</value>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>
I am using Spring 3.2.3.RELEASE as well. I realize this post is over 4 months old but I figured someone might find it useful.
Short form: What is the proper way to load an implementation of: BeanDefinitionRegistryPostProcessor?
Expanded form: Is there a way to load BeanDefinitionRegistryPostProcessor before any beans have been created. If you look at the javadoc:
Extension to the standard {#link BeanFactoryPostProcessor} SPI, allowing for
the registration of further bean definitions before regular
BeanFactoryPostProcessor detection kicks in.
So it's meant to be loaded when bean definitions have been created but before any beans have been created. If we just create it as a regular bean in the application xml then it defeats the purpose of having this bean in the first place.

Trouble integrating a library using Apache Commons Config with Spring project using PropertyPlaceholderConfigurer

I have a custom library that sends a JMS notification. The idea is that you can use the library by simply by setting some properties in your project.
The custom library is using the Apache Commons Configuration. (The reason I mention this is that I suspect it may be playing a role in my problem). I'm trying to use it in my current Spring project which doesn't use Commons Config but uses the PropertyPlaceholderConfigurer. I've got the properties I need written out in a .properties file.
I've added the library as a dependency in my pom.xml file. I tried adding it into my web.xml like so
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
...
classpath:mylibrary-context.xml
</param-value>
</context-param>
When creating a bean in my Spring project, I tried to reference one of the beans defined in the library, so I could use it. There's an error creating one of the library beans that requires a property from my properties file.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'libraryBean' defined in class path resource [mylibrary-context.xml]: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'java.lang.String' to required type 'boolean' for property 'useCompression'; nested exception is java.lang.IllegalArgumentException: Invalid boolean value [${foo.bar.use.compression}]
I know some of the other properties files in the project have boolean properties set the exact same way without issue. Here is how I'm loading the properties files in the applicationContext.xml(which after checking the logs, seems to load correctly)
<bean id="propertyPlaceholderConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
...
<!-- loaded in order-->
<value>classpath:foo/bar/some-props.properties</value>
<value>classpath:foo/bar/some-monitoring.properties</value>
<value>file:/${user.home}/foo/bar/some-monitoring.properties</value>
<value>file:/etc/foo/bar/some-monitoring.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
and finally my some-monitoring.properties file:
foo.bar.use.compression=true
foo.bar.name=foobar
foo.bar.broker.url=someurl
So I'm just wondering what's going on with the boolean property and why? If its a general problem with the PropertyPlaceholderConfigurer not loading boolean properties, its odd that I'm not having a problem with the other properties files. Is it that there is some conflict because of the library using the Commons Configuration? My knowledge of how Spring loads up these properties is pretty shallow so this seems like a chance to get a better handle on what's going on under the covers.
If anyone can shed some light on what's going on and how to fix/get around it, any help is greatly appreciated!
Quoting Jurgen on the spring-dev list:
PropertyPlaceholderConfigurer is an implementation of the BeanFactoryPostProcessor interface: This interface and its sibling
BeanPostProcessor just apply to the BeanFactory that defines them,
that is, to the application context that defines them.
If you combine multiple config files into a single
contextConfigLocation, a PropertyPlaceholderConfigurer defined in any
of the files will apply to all of the files, because they are loaded
into a single application context.
As far as I understand, you tried to combine all your application contexts into the contextConfigLocation. According to the quotation, it should work fine. I think that you have other kind of problem. In my opinion both boolean and string properties are not loaded at all. Maybe it seems as string properties are loaded, but I think that their values are set to ${foo.bar.name} and ${foo.bar.broker.url}. Since they are Strings, they can hold these values. When Spring tries to set ${foo.bar.use.compression} to the boolean, you have typecast problem which just reveals wider issue.
Probably, if you set:
<property name="ignoreResourceNotFound" value="false"/>
<property name="ignoreUnresolvablePlaceholders" value="false"/>
you will see that PropertyPlaceHolderConfigurer cannot resolve your properties files.
Please also try using classpath*: notation when you refer to resources from other JARs.

external config based on context path

I would like to deploy multiple independent copies of a particular web-app on the same tomcat server under different context paths. Each web-app will need different configuration settings (database name, password, etc), but I would like to keep the wars exactly identical.
My plan was to have the app figure out its context path on startup, then read a specific .properties file outside of tomcat identified by the context path. For example, if a war was deployed to {tomcat path}/webapps/pineapple, then I would want to read /config/pineapple.properties
I've been trying to find a way to inject an instance of ServletContext via spring (3), but all the advice I've seen so far use the deprecated ServletContextFactoryBean.
Is there a better way to get the context path injected or better way to load external files based on the context path?
With the help of ServletContextAttributeFactoryBean and Spring EL, you can reference ServletContext init parameters (<context-param> in web.xml) like that:
#{contextAttributes.myKey}
This allows you to use PropertyPlaceHolderConfigurer and load property files from arbitrary, user-defined locations:
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="#{contextParameters.APP_HOME}/conf/app.properties"/>
</bean>
The corresponding definition of the ServletContext init parameter in Tomcat's context.xml:
<Parameter name="APP_HOME" value="file:/test" override="false"/>
Or in your app's web.xml:
<context-param>
<param-name>APP_HOME</param-name>
<param-value>file:/test</param-value>
</context-param>
This should be the solution.
<bean name="envConfig" class="EnvironmentConfiguration">
<property name="locations">
<list>
<value>file:///#{servletContext.contextPath}.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
Extend Propertyplaceholderconfigurer to use DB to pick up the values. Example here
Load the actual values of the settings (database name, password etc) to the db as part of seed data
When your web-app's app ctx is being initialized, the properties are resolved from the DB
This is the approach we have been following and works great. If you can switch to Spring 3.1 then it has support for Environment Profiles which may be useful for you.

global properties in spring

is it possible to define in a spring context file, and one or more properties that
can be accessed in <bean> elements.
The example below illustrates best what I need - I want to define the property FOO once and then reference it multiple times in my various <bean> definitions:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<properties>
<property name="FOO" value="BAR">
</properties>
<bean name="TEST" class="mytest">
<property name="MYFOO" value="${FOO}"/>
</bean>
<bean name="TEST1" class="mytest1">
<property name="MYFOO" value="${FOO}"/>
</bean>
</beans>
Any input would be much appreciated.
Thanks, Kevin.
You can do this using the snappily-named PropertyPlaceHolderConfigurer. See here for the example in the spring docs. You don't define the property values themselves in the spring beans file, you externalise them in a .properties file.
You could, I suspect, use PropertyPlaceHolderConfigurer or one of its siblings to inject a Properties object defined inside your Spring file, but that would be a rather more verbose solution.
If you only need to define them in your xml file and never change them (like you would change a configuration option, say a port or an ip), you can create a bean of class String with the given property and then write . Creating a bean of class String isn't the most straight forward thing, but it's doable.
If you want something more complicated (say you want a configuration file residing outside the jar where your xml with the bean definition is), you can look at the PropertyPlaceholderConfigurer class.
Just found a way to do this all in the spring XML file with no external properties file needed (though you can supplement with one if desired)
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<util:properties>
<prop key="foo1">bar1</prop>
<prop key="foo2">bar2</prop>
</util:properties>
</property>
</bean>
then you reference them just like normal as ${foo1} where needed

Resources