A word on PropertyPlaceholderConfigurer
A few days back one of my juniors came to me and said that when he test his modules separately in JUnit that worked perfectly but when the modules were deployed in Server as a unit, Server fails to start.
So I checked his code and fortunately identified the problem.
in this Article, I will discuss that problem.
Let me describe the problem in detail.
Problem Statement:
The application he works on has three layers.
1.Dao layers
2.Service layers.
3.Middleware(Spring MVC).
Which is very standard architecture, and each layer built on top of spring so maintains separate Spring context files.
Each layer maintains its own property file.
Say for Dao layers database connection related properties are maintained in db.properties.
In Service layer, web service URL or other service parameters are maintained in service.properties file.
To load these properties files in application it uses PropertyPlaceholderConfigurer in each SpringContext file (Dao, Service, Middleware).
Sample example
db.properties
db.url=abc@localhost:1028/BillSchema
db.user=admin
db.password=tiger
and the entry for PropertyPlaceholderConfigurer in Spring_DB_Context.xml
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:db.properties</value>
</list>
</property>
</bean>
<property name="locations">
<list>
<value>classpath:db.properties</value>
</list>
</property>
</bean>
Same for Services
Service.properties
bil.service.url =http://localhost:9292/billing/getTaxBasedOnState?state=
bil.user=admin.
bill.passwd=abcd
and the entry for PropertyPlaceholderConfigurer in Spring_Service_Context.xml
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:service.properties</value>
</list>
</property>
</bean>
<property name="locations">
<list>
<value>classpath:service.properties</value>
</list>
</property>
</bean>
In middleware, Spring context Spring_Middleware_Context.xml imports Spring_DB_Context.xml and Spring_Service_Context.xml
Now when he runs each modules Junit test it can resolve the properties files and works fine, but the problem kicks in when middleware application combine other two modules as jar and import context files into Spring_middleware context and try to deploy in Server.
It Shows an error message {db.url} can’t resolve, and server fails to start.
So, His question is why it is happening?
In Junit, it runs but when we clubbed two modules as a jar and try to deploy it on server why such error message, as we have provided the PropertyPlaceholderConfigurer in each context and both properties files are present in classpath.
Actual Problem:
Upon viewing the error message one thing is clear that somehow those properties are not resolved by PropertyPlaceholderConfigurer.
Now deep dive to Spring context to understand why it is not resolved?
There are three separate Spring context files
Spring_DB_Context.xml
Spring_Service_Context.xml
Spring_Middleware_Context.xml
Spring_DB_Context.xml,Spring_Service_Context.xml joins the big context Spring_Middleware_Context.xml while deploying, via import statement in Spring_Middleware_Context.xml.
And each context has it’s own PropertyPlaceholderConfigurer and a property file. Now, if the Service context is loaded first by classloader then it’s PropertyPlaceholderConfigurer is loaded and it properties files so this PropertyPlaceholderConfigurer can resolve the beans under Service context but won’t able to resolve the properties of DB_Context as Service PropertyPlaceholderConfigurer does not knows about that db.properties file. So server says {db.url } can’t resolved.
Same is true for service layer if classloader load Db Context first the {service.url} can’t be resolved.
Solution
The solution is just a tweak in PropertyPlaceholderConfigurer in each context. Just add the following line
<property name="ignoreUnresolvablePlaceholders" value="true"/>
So, Updated context files
Service context
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:service.properties</value>
</list>
</property>
<property name="locations">
<list>
<value>classpath:service.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>
</bean>
DB context
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:dbproperties</value>
</list>
</property>
<property name="locations">
<list>
<value>classpath:dbproperties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>
</bean>
By doing this we tell Spring to ignore the unresolved properties or put it in another word
By default, Spring throws an exception if it can’t resolve a property that is fail-fast in nature.
But here we will explicitly say to Spring don’t throw the exception but go on with unresolved properties defer the action so when other PropertyPlaceholderConfigurer(Db Context) will be loaded it will resolve the other part.
NB: Please note that for each context you have to mention
<property name="ignoreUnresolvablePlaceholders" value="true"/>, unless if one context does not provide aforesaid tag it will fails there immediately as by default Spring is fail-fast.
My juniors case was very similar like this although he provides <property name="ignoreUnresolvablePlaceholders" value="true"/> in every modules still it fails.
Then I search for transitive dependencies on its module and found that one of the modules does not define <property name="ignoreUnresolvablePlaceholders" value=" true"/> tag in its context so it fails there, we update the same and recompile it then all problem just gone like magic.
TIP: Always use <property name="ignoreUnresolvablePlaceholders" value="true"/>, with PropertyPlaceholderConfigurer as you never know when and how other modules use your modules and they may have their own PropertyPlaceholderConfigurer as well, So you can break their code unknowingly and give them an opportunity to scratch their head.