Spring 3.1’s PropertyPlaceholderConfigurer

Spring 3.1 brought a lot of good changes to the framework but with any version change, behaviors can be different. Spring does a good job documenting most of this API changes but there is one that I apparently missed or underestimated the impact of.

Property files can be “imported” into the Spring context so that the values can be inserted into configuration using ${ } with the property value inside of the brackets. This is a handy feature that I use frequently in my Spring projects. The PropertyPlaceholderConfigurer has been replaced by PropertySourcesPlaceholderConfigurer in Spring 3.1. One minor difference between them is the latter has the ability to read @Value annotations for direct injection of property values. The other side effect is that the 3.1 class puts system properties ahead of your property file’s values. 3.0, on the other hand, let your property file win if a name matched one as a system parameter.

I ran into this problem because my 3.0 configuration had a replacement parameter for a data source name injected by JNDI. The JNDI name is different between environments at my client, so I had to parameterize it. In Spring 3.0, my configuration was as so:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.company.app" />
<context:property-placeholder location="classpath:META-INF/dev.runtime.properties" />
<import resource="classpath:/com/company/app/applicationContext-dao.xml" />
<import resource="classpath:/com/company/app/applicationContext-jndi.xml" />
<import resource="classpath:/com/company/app/applicationContext-mail.xml" />
<import resource="classpath:/com/company/app/dev.applicationContext-mi.xml" />
</beans>

You’ll notice the context:property-placeholder element has a single properties file. The values read in are:

echoDataSource=EchoDataSource
pavDataSource=pavDataSource
advertDataSource=AdvertOdsDataSource
sortedOdsDataSource=SortedOdsDataSource

and they are used in my JNDI configuration:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
<jee:jndi-lookup id="echoDataSource" jndi-name="${echoDataSource}" />
<jee:jndi-lookup id="pavDataSource" jndi-name="${pavDataSource}" />
<jee:jndi-lookup id="advertOdsDataSource" jndi-name="${advertOdsDataSource}" />
<jee:jndi-lookup id="sortedOdsDataSource" jndi-name="${sortedOdsDataSource}" />
</beans>

When I switched to 3.1, I updated the XML namespace to point at 3.1. This changes things behind the scenes to go to PropertySourcesPlaceholderConfigurer. If you want to retain the original 3.0 behavior, you can keep the XMLNS at 3.0 or add system-properties-mode=”FALLBACK”.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.company.app" />
<context:property-placeholder system-properties-mode="FALLBACK"
location="classpath:META-INF/dev.runtime.properties" />
<import resource="classpath:/com/company/app/applicationContext-dao.xml" />
<import resource="classpath:/com/company/app/applicationContext-jndi.xml" />
<import resource="classpath:/com/company/app/applicationContext-mail.xml" />
<import resource="classpath:/com/company/app/dev.applicationContext-mi.xml" />
</beans>
view raw spring-context.xml hosted with ❤ by GitHub

I suspected something was happening with the behavior when I saw in the log files that the data source couldn’t be cast as a string. I pulled out my hair trying to figure it out and ended up renaming pavDataSource to PavDataSource and it worked fine. This problem came back up deploying to another environment having the lower case name and I have no ability to change it. Switching back to 3.0 behavior fixed this because I suspect a system parameter was being created with pavDataSource as the name.

####<Feb 17, 2012 11:42:53 AM EST> <Warning> <HTTP> <server> <spaServer01> <[ACTIVE] ExecuteThread: '62' for queue: 'weblogic.kernel.
Default (self-tuning)'> <<WLS Kernel>> <> <> <1329496973873> <BEA-101162> <User defined listener org.springframework.web.context.ContextLoaderListener failed
: org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'pavDataSource' defined in null: Cannot convert value [we
blogic.jdbc.common.internal.RmiDataSource@320a123] from source type [RmiDataSource] to target type [String].
org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'pavDataSource' defined in null: Cannot convert value [webl
ogic.jdbc.common.internal.RmiDataSource@320a123] from source type [RmiDataSource] to target type [String]
at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:209)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.processProperties(PropertySourcesPlaceholderConfigurer.java:174)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.postProcessBeanFactory(PropertySourcesPlaceholderConfigurer.java:151)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:681)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:656)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:446)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:384)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111)
at weblogic.servlet.internal.EventsManager$FireContextListenerAction.run(EventsManager.java:465)
at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
at weblogic.security.service.SecurityManager.runAs(Unknown Source)
at weblogic.servlet.internal.EventsManager.notifyContextCreatedEvent(EventsManager.java:175)
at weblogic.servlet.internal.WebAppServletContext.preloadResources(WebAppServletContext.java:1784)
at weblogic.servlet.internal.WebAppServletContext.start(WebAppServletContext.java:2999)
at weblogic.servlet.internal.WebAppModule.startContexts(WebAppModule.java:1371)
at weblogic.servlet.internal.WebAppModule.start(WebAppModule.java:468)
at weblogic.application.internal.flow.ModuleStateDriver$3.next(ModuleStateDriver.java:204)
at weblogic.application.utils.StateMachineDriver.nextState(StateMachineDriver.java:37)
at weblogic.application.internal.flow.ModuleStateDriver.start(ModuleStateDriver.java:60)
at weblogic.application.internal.flow.ScopedModuleDriver.start(ScopedModuleDriver.java:200)
at weblogic.application.internal.flow.ModuleListenerInvoker.start(ModuleListenerInvoker.java:117)
at weblogic.application.internal.flow.ModuleStateDriver$3.next(ModuleStateDriver.java:204)
at weblogic.application.utils.StateMachineDriver.nextState(StateMachineDriver.java:37)
at weblogic.application.internal.flow.ModuleStateDriver.start(ModuleStateDriver.java:60)
at weblogic.application.internal.flow.StartModulesFlow.activate(StartModulesFlow.java:27)
at weblogic.application.internal.BaseDeployment$2.next(BaseDeployment.java:635)
at weblogic.application.utils.StateMachineDriver.nextState(StateMachineDriver.java:37)
at weblogic.application.internal.BaseDeployment.activate(BaseDeployment.java:212)
at weblogic.application.internal.EarDeployment.activate(EarDeployment.java:16)
at weblogic.application.internal.DeploymentStateChecker.activate(DeploymentStateChecker.java:162)
at weblogic.deploy.internal.targetserver.AppContainerInvoker.activate(AppContainerInvoker.java:79)
at weblogic.deploy.internal.targetserver.operations.AbstractOperation.activate(AbstractOperation.java:569)
at weblogic.deploy.internal.targetserver.operations.ActivateOperation.activateDeployment(ActivateOperation.java:140)
at weblogic.deploy.internal.targetserver.operations.ActivateOperation.doCommit(ActivateOperation.java:106)
at weblogic.deploy.internal.targetserver.operations.AbstractOperation.commit(AbstractOperation.java:323)
at weblogic.deploy.internal.targetserver.DeploymentManager.handleDeploymentCommit(DeploymentManager.java:820)
at weblogic.deploy.internal.targetserver.DeploymentManager.activateDeploymentList(DeploymentManager.java:1227)
at weblogic.deploy.internal.targetserver.DeploymentManager.handleCommit(DeploymentManager.java:436)
at weblogic.deploy.internal.targetserver.DeploymentServiceDispatcher.commit(DeploymentServiceDispatcher.java:163)
at weblogic.deploy.service.internal.targetserver.DeploymentReceiverCallbackDeliverer.doCommitCallback(DeploymentReceiverCallbackDeliverer.java:181)
at weblogic.deploy.service.internal.targetserver.DeploymentReceiverCallbackDeliverer.access$100(DeploymentReceiverCallbackDeliverer.java:12)
at weblogic.deploy.service.internal.targetserver.DeploymentReceiverCallbackDeliverer$2.run(DeploymentReceiverCallbackDeliverer.java:67)
at weblogic.work.SelfTuningWorkManagerImpl$WorkAdapterImpl.run(SelfTuningWorkManagerImpl.java:516)
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:201)
at weblogic.work.ExecuteThread.run(ExecuteThread.java:173)
>
view raw server.log hosted with ❤ by GitHub

Confusing and frustrating as hell, but the simple mode switch made it right as rain.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s