I recently upgraded a project from 2.2.3.GA to 2.3.6.FINAL and encountered a bug that appears to originate in the SpringBeanProcessor and which breaks property placeholder variable replacement in certain spring beans.
I haven't completely unwound the bug to the root cause, but I've gotten close
enough to be confident that someone with more resteasy knowledge can likely
address it much more quickly than I can.
First, the symptoms:
I have a bean of class org.springframework.jndi.JndiTemplate declared as
follows:
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop
key="java.naming.factory.initial">com.sonicsw.jndi.mfcontext.MFContextFactory</prop>
<prop key="java.naming.provider.url">${jndi.url}</prop>
<prop key="java.naming.security.principal">${jndi.user}</prop>
<prop key="java.naming.security.credentials">${jndi.password}</prop>
<prop key="com.sonicsw.jndi.mfcontext.domain">${jndi.domain}</prop>
<prop key="clientId">${jndi.clientId}</prop>
<prop key="subscriptionDurable">true</prop>
<prop
key="durableSubscriptionName">${jndi.durableSubscriptionName}</prop>
<prop key="timeToLive">${jndi.timeToLive}</prop>
<prop
key="com.sonicsw.jndi.mfcontext.idleTimeout">${jndi.timeout}</prop>
</props>
</property>
</bean>
I have a property placeholder that is most definitely initialized with values
for all of those properties. I can set the same values in a test object and
see that they are correctly replaced. However, for reasons that I'll get into
in a moment, the jndiTemplate properties are NOT replaced and it receives the
raw ${...} strings instead. Needless to say, this breaks my project.
Switching back to 2.2.3.GA definitely fixes it without making any other changes.
What I've discovered so far about the cause:
In SpringBeanProcessor.postProcessBeanFactory(), a method called
findResteasyRegistrations() is called. That method, in turn, calls the
following:
beanFactory.getBeansOfType(ResteasyRegistration.class);
That method call causes some beans (but not all beans) to be instantiated, and
any beans that are instantiated at that point do not have property placeholder
values replaced in them, because the property placeholder processor has
(apparently) not yet executed. What I haven't figured out is why my
jndiTemplate bean is impacted by this, while another bean that I created simply
to test the creation of properties inside of a bean definition is not. It is
apparently skipped over by the getBeansOfType(ResteasyRegistration.class) and
property replacement happens correctly in that case.
Initially, I suspected that this was a problem with post processor ordering.
This was somewhat vexing as the SpringBeanProcessor does implement the
PriorityOrdered interface, but exposes no mechanism for modifying the order
value. I created my own version of SpringContextLoaderListener which forgoes
the use of SpringContextLoaderSupport and performs the same work, but also
calls setOrder(x) on the SpringBeanProcessor before adding it to the
postprocessors and application listeners. However, that didn't seem to have
any impact, no matter in which relative order I placed the property placeholder
and the spring bean processor.
For one final data point, if I don't use a property placeholder, but instead
reference a properties object via Spring-EL in the XML, then the values are
correctly replaced, because the spring-el is evaluated when the xml is parsed
(I assume), while property placeholders are definitely evaluated lazily, long
after the bean definitions are created by the xml parsing process.
<util:properties id="jmsProperties"
location="file:${catalina.base}/conf/jms.properties"/>
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop
key="java.naming.factory.initial">com.sonicsw.jndi.mfcontext.MFContextFactory
</prop>
<prop key="java.naming.provider.url">#{jmsProperties['jndi.url']}</prop>
<prop
key="java.naming.security.principal">#{jmsProperties['jndi.user']}</prop>
<prop
key="java.naming.security.credentials">#{jmsProperties['jndi.password']}</prop>
<prop
key="com.sonicsw.jndi.mfcontext.domain">#{jmsProperties['jndi.domain']}</prop>
<prop key="clientId">#{jmsProperties['jndi.clientId']}</prop>
<prop key="subscriptionDurable">true</prop>
<prop
key="durableSubscriptionName">#{jmsProperties['jndi.durableSubscriptionName']}</prop>
<prop key="timeToLive">#{jmsProperties['jndi.timeToLive']}</prop>
<prop
key="com.sonicsw.jndi.mfcontext.idleTimeout">#{jmsProperties['jndi.timeout']}</prop>
</props>
</property>
</bean>
This works, but is a kludge. I'd much prefer to have property-placeholding
actually functional, especially since it is apparently impossible to predict
which beans will fail to have properties replaced. Looking at the sourec code
to jndiTemplate, I can see no obvious reason why it is treated differently than
my own test bean. Both have no annotations and both receive a
java.util.Properties object as a property. The behavior is consistent no
matter what order they appear in my context xml file.
I've attached my replacement SpringContextLoaderListener just for reference, in
case someone else wants to modify the ordering of the placeholders while
testing or fixing this. The relevant web.xml entries are below:
<context-param>
<param-name>resteasy.postprocessor.order</param-name>
<param-value>0</param-value>
</context-param>
<listener>
<listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
<!--
listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class-->
<listener-class>
com.westernasset.compliance.util.SpringContextLoaderListener
</listener-class>
</listener>
**********************************************************************
E-mail sent through the Internet is not secure. Western Asset
therefore recommends that you do not send any confidential or
sensitive information to us via electronic mail, including social
security numbers, account numbers, or personal identification
numbers. Delivery, and or timely delivery of Internet mail is not
guaranteed. Western Asset therefore recommends that you do not send
time sensitive or action-oriented messages to us via electronic
mail.
**********************************************************************
SpringContextLoaderListener.java
Description: SpringContextLoaderListener.java
------------------------------------------------------------------------------ Precog is a next-generation analytics platform capable of advanced analytics on semi-structured data. The platform includes APIs for building apps and a phenomenal toolset for data science. Developers can use our toolset for easy data analysis & visualization. Get a free account! http://www2.precog.com/precogplatform/slashdotnewsletter
_______________________________________________ Resteasy-users mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/resteasy-users
