|
From: Solomon D. <sd...@gm...> - 2013-04-18 12:30:20
|
Samuel,
I submitted a merge request for this. I think that there would be great
value if you (and others with the same problem) would test out the fix. We
have some excellent unit tests, but this problem relates to subtleties in
the Spring lifecycle and requires situation specific testing. If your
testing is successful, perhaps we can work on creating an additional unit
test for resteasy.
-Solomon
On Wed, Apr 17, 2013 at 10:33 PM, Solomon Duskis <sd...@gm...> wrote:
> Thank you for the careful analysis. I'll see if I can work around this
> issue using other approaches relating to the Spring lifecycle. I fixed a
> similar issue a while back with @Providers, and might be able to use the
> same approach for ResteasyRegistration.
>
> I hope to check in something tomorrow. Since this is a nuanced issue, I
> think that this change would benefit from field testing. Would you feel
> comfortable with being beta testers for this change?
>
> -Solomon
>
>
> On Wed, Apr 17, 2013 at 7:37 PM, Gendler, Samuel <
> Sam...@we...> wrote:
>
>> 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:* 'res...@li...'
>> *Cc:* 'res...@li...'
>> *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:Sam...@we...<Sam...@we...>]
>>
>> *Sent:* Wednesday, April 17, 2013 3:36 PM
>> *To:* 'res...@li...'
>> *Cc:* 'res...@li...'
>> *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-developers mailing list
>> Res...@li...
>> https://lists.sourceforge.net/lists/listinfo/resteasy-developers
>>
>>
>
|