So it proved to be even more subtle than just the existence of a factory bean.  
It has to be a factory bean that doesn't declare the type of objects it will 
create - which is probably limited only to things like JndiObjectFactoryBean.  
The FactoryBean interface declares a method called getObjectType() which is 
explicitly supposed to return null if the type of the object is not known in 
advance.  The following is from the javadoc for FactoryBean:

Returns:
the type of object that this FactoryBean creates, or null if not known at the 
time of the call

When I created a factory bean that specifies a type in getObjectType(), the 
properties were not initialized during the SpringBeanProcessor's call to 
getBeansOfType().  When I created a factory bean that returns null, my 
properties get initialized with the values before property placeholder has done 
its thing because it actually asks the factory for the bean in order to 
determine the type, so it must initialize it first.  I'm not quite sure what 
the fix to this would be, since the logic of what is going on in 
SpringBeanProcessor isn't entirely clear to me, but it does appear to be a bug 
on the resteasy side of things.  Somehow, this code needs to execute after 
property placeholder replacement (this seems to be the case in resteast 
2.2.3.GA, which still contains calls to getBeansOfType(), but doesn't trigger 
this bug, so something is happening to change the order of events).

From: Gendler, Samuel
Sent: Wednesday, April 17, 2013 4:05 PM
To: 'resteasy-develop...@lists.sourceforge.net'
Cc: 'resteasy-users@lists.sourceforge.net'
Subject: RE: spring bean processor bug

One further piece of info - the javadoc for getBeansOfType() says that 
factories will be initialized in order to check the type of returned objects.  
My jndiTemplate is referenced by a bean created via a factory, and I suspect 
that this is why it is causing the jndiTemplate to be initialized while beans 
that have identical properties but are not used by factories are not 
initialized until much later and only after property placeholder replacement 
has occurred.  I haven't actually verified this hypothesis, but it seems 
likely.  My factory beans are declared as follows:

  <bean id="sonicJmsTradeConnectionFactory" 
class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
    <property name="targetConnectionFactory">
      <bean class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="jndiTemplate" />
        <property name="jndiName">
          <value>${jndi.ConnectionFactory}</value>
        </property>
      </bean>
    </property>
    <property name="username">
      <value>${jndi.user}</value>
    </property>
    <property name="password">
      <value>${jndi.password}</value>
    </property>
  </bean>

  <bean id="destination" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiTemplate" ref="jndiTemplate" />
    <property name="jndiName">
      <value>${jndi.TopicName}</value>
    </property>
  </bean>

My guess is that it is actually incorrect to utilize the getBeansOfType() 
methods in an application context which is not yet completely initialized.  At 
the same time, I don't really understand why setting the order to 
LOWEST_PRECEDENCE doesn't fix the problem unless something internal to spring 
is ignoring the suggested ordering.

From: Gendler, Samuel [mailto:samuel.gend...@westernasset.com]
Sent: Wednesday, April 17, 2013 3:36 PM
To: 'resteasy-develop...@lists.sourceforge.net'
Cc: 'resteasy-users@lists.sourceforge.net'
Subject: [Resteasy-developers] spring bean processor bug

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. 
**********************************************************************
------------------------------------------------------------------------------
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
Resteasy-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/resteasy-users

Reply via email to