View Full Version : Tomcat / JaxWS / Spring Problem
TimGoodacre
Apr 25th, 2006, 11:08 AM
I've inherited a tomcat web service app using JaxWS and I've been trying to integrate spring into it. It is mostly going well but I'm having a problem that I have already spent a day trying to solve. I couldn't find anything relevant to it on the web...
I'm using tomcat 5.5.16, Spring 1.2.7, and JaxWs (jwsdp) 2.0.
I'm trying to use the ContextLoaderListener but it is loading 2 instances of my Spring context into memory at startup.
The Web.xml is:
<web-app>
<!-- Context Configuration locations for Spring XML files -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beanRefFactory.xml</param-value>
</context-param>
<context-param>
<param-name>locatorFactorySelector</param-name>
<param-value>classpath:beanRefFactory.xml</param-value>
</context-param>
<context-param>
<param-name>parentContextKey</param-name>
<param-value>tradeserver-context</param-value>
</context-param>
<display-name>REFORM Trade Service</display-name>
<description>REFORM Trade Service</description>
<listener>
<listener-class>
com.sun.xml.ws.transport.http.servlet.WSServletCon textListener
</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListe ner</listener-class>
</listener>
<servlet>
<servlet-name>TradeService10</servlet-name>
<display-name>REFORM Trade Service 1.0</display-name>
<description>REFORM Trade Service 1.0</description>
<servlet-class>
com.sun.xml.ws.transport.http.servlet.WSServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>TradeService10</servlet-name>
<url-pattern>/service/1.0</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>60</session-timeout>
</session-config>
</web-app>
The beanRefFactory.xml file is straightforward:
<beans>
<bean id="tradeserver-context" class="org.springframework.context.support.ClassPathXmlAp plicationContext">
<constructor-arg>
<list>
<value>tradeserverspring.xml</value>
</list>
</constructor-arg>
</bean>
</beans>
So when the app starts up it immediately loads 2 instances of everything in the "tradeserverspring.xml" file.
I have some glue code in the Web Service proxy class that looks like:
BeanFactoryLocator bfLocator = ContextSingletonBeanFactoryLocator.getInstance("classpath:beanRefFactory.xml");
BeanFactoryReference bfReference = bfLocator.useBeanFactory("tradeserver-context");
BeanFactory factory = bfReference.getFactory();
tsImpl = (TradeService10)factory.getBean("tradeService10Impl");
This is happily re-using one of the loaded spring contexts...
Any help/advice would be more than welcome at this stage.
Thanks,
Tim
TimGoodacre
Apr 25th, 2006, 11:39 AM
A bit more info:
If I comment out the following line:
<context-param>
<param-name>locatorFactorySelector</param-name>
<param-value>classpath:beanRefFactory.xml</param-value>
</context-param>
...from the Web.xml, the ContextLoaderListener only loads one spring context in, but the glue code snippet in the 1st post then loads another...
Confused :confused:
TimGoodacre
Apr 26th, 2006, 03:16 AM
Some more info still: This is part of the startup log that relates to the Spring context loading. I've highlighted the lines where each context load appears to begin - maybe there will be something obvious there to someone:
09:13:15,249 INFO ContextLoader:174 - Root WebApplicationContext: initialization started
09:13:15,264 INFO ContextLoader:315 - Getting parent context definition: using parent context key of 'tradeserver-context' with BeanFactoryLocator
09:13:15,374 INFO CollectionFactory:66 - JDK 1.4+ collections available
09:13:15,421 INFO XmlBeanDefinitionReader:163 - Loading XML bean definitions from class path resource
09:13:15,483 INFO ClassPathXmlApplicationContext:92 - Bean factory for application context [org.springframework.context.support.ClassPathXmlAp plicationContext;hashCode=4406154]: org.springframework.beans.factory.support.DefaultL istableBeanFactory defining beans [tradeserver-context]; root of BeanFactory hierarchy
09:13:15,499 INFO ClassPathXmlApplicationContext:290 - 1 beans defined in application context [org.springframework.context.support.ClassPathXmlAp plicationContext;hashCode=4406154]
09:13:15,514 INFO ClassPathXmlApplicationContext:432 - Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMess ageSource@5bfbd8]
09:13:15,530 INFO ClassPathXmlApplicationContext:454 - Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicatio nEventMulticaster@f33280]
09:13:15,530 INFO DefaultListableBeanFactory:268 - Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultL istableBeanFactory defining beans [tradeserver-context]; root of BeanFactory hierarchy]
09:13:15,608 INFO XmlBeanDefinitionReader:163 - Loading XML bean definitions from class path resource [tradeserverspring.xml]
09:13:15,827 INFO ClassPathXmlApplicationContext:92 - Bean factory for application context [org.springframework.context.support.ClassPathXmlAp plicationContext;hashCode=17368622]: org.springframework.beans.factory.support.DefaultL istableBeanFactory defining beans [mBeanServer,jmxHtmlAdapter,tradeService10Impl,trad eDao,priceDao,staticDataDao,transactionManager,end urDataSource,tradeService10XmlWriterManager]; root of BeanFactory hierarchy
09:13:15,827 INFO ClassPathXmlApplicationContext:290 - 9 beans defined in application context [org.springframework.context.support.ClassPathXmlAp plicationContext;hashCode=17368622]
09:13:15,827 INFO ClassPathXmlApplicationContext:432 - Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMess ageSource@9e0c79]
09:13:15,827 INFO ClassPathXmlApplicationContext:454 - Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicatio nEventMulticaster@45064f]
09:13:15,827 INFO DefaultListableBeanFactory:268 - Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultL istableBeanFactory defining beans [mBeanServer,jmxHtmlAdapter,tradeService10Impl,trad eDao,priceDao,staticDataDao,transactionManager,end urDataSource,tradeService10XmlWriterManager]; root of BeanFactory hierarchy]
09:13:15,858 DEBUG JmxHtmlAdaptorBean:21 - Creating JMX HTML Adaptor on port 8091
09:13:15,889 DEBUG TradeService10Impl:44 - TradeService10Impl created
09:13:16,233 DEBUG TradeService10Impl:21 - Registering MBean [TestDomain:Name=TradeServer10]
09:13:16,233 DEBUG TradeService10Impl:24 - 3 MBeans registered
[B]09:13:16,296 INFO XmlBeanDefinitionReader:163 - Loading XML bean definitions from class path resource [beanRefFactory.xml]
09:13:16,311 INFO XmlWebApplicationContext:92 - Bean factory for application context [Root WebApplicationContext]: org.springframework.beans.factory.support.DefaultL istableBeanFactory defining beans [tradeserver-context]; parent: org.springframework.beans.factory.support.DefaultL istableBeanFactory defining beans [mBeanServer,jmxHtmlAdapter,tradeService10Impl,trad eDao,priceDao,staticDataDao,transactionManager,end urDataSource,tradeService10XmlWriterManager]; root of BeanFactory hierarchy
09:13:16,311 INFO XmlWebApplicationContext:290 - 1 beans defined in application context [Root WebApplicationContext]
09:13:16,311 INFO XmlWebApplicationContext:432 - Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMess ageSource@12b6037]
09:13:16,311 INFO XmlWebApplicationContext:454 - Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicatio nEventMulticaster@b9e9a3]
09:13:16,327 INFO UiApplicationContextUtils:83 - Unable to locate ThemeSource with name 'themeSource': using default [org.springframework.ui.context.support.ResourceBun dleThemeSource@19a0c4e]
09:13:16,327 INFO DefaultListableBeanFactory:268 - Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultL istableBeanFactory defining beans [tradeserver-context]; parent: org.springframework.beans.factory.support.DefaultL istableBeanFactory defining beans [mBeanServer,jmxHtmlAdapter,tradeService10Impl,trad eDao,priceDao,staticDataDao,transactionManager,end urDataSource,tradeService10XmlWriterManager]; root of BeanFactory hierarchy]
09:13:16,327 INFO XmlBeanDefinitionReader:163 - Loading XML bean definitions from class path resource [tradeserverspring.xml]
09:13:16,358 INFO ClassPathXmlApplicationContext:92 - Bean factory for application context [org.springframework.context.support.ClassPathXmlAp plicationContext;hashCode=5503135]: org.springframework.beans.factory.support.DefaultL istableBeanFactory defining beans [mBeanServer,jmxHtmlAdapter,tradeService10Impl,trad eDao,priceDao,staticDataDao,transactionManager,end urDataSource,tradeService10XmlWriterManager]; root of BeanFactory hierarchy
09:13:16,358 INFO ClassPathXmlApplicationContext:290 - 9 beans defined in application context [org.springframework.context.support.ClassPathXmlAp plicationContext;hashCode=5503135]
09:13:16,358 INFO ClassPathXmlApplicationContext:432 - Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMess ageSource@1eb41d6]
09:13:16,358 INFO ClassPathXmlApplicationContext:454 - Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicatio nEventMulticaster@2f892f]
09:13:16,358 INFO DefaultListableBeanFactory:268 - Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultL istableBeanFactory defining beans [mBeanServer,jmxHtmlAdapter,tradeService10Impl,trad eDao,priceDao,staticDataDao,transactionManager,end urDataSource,tradeService10XmlWriterManager]; root of BeanFactory hierarchy]
09:13:16,358 DEBUG JmxHtmlAdaptorBean:21 - Creating JMX HTML Adaptor on port 8091
09:13:16,358 DEBUG TradeService10Impl:44 - TradeService10Impl created
09:13:16,374 DEBUG TradeService10Impl:21 - Registering MBean [TestDomain:Name=TradeServer10]
09:13:16,374 DEBUG TradeService10Impl:24 - 3 MBeans registered
09:13:16,374 INFO ContextLoader:189 - Using context class [org.springframework.web.context.support.XmlWebAppl icationContext] for root WebApplicationContext
09:13:16,374 INFO ContextLoader:199 - Root WebApplicationContext: initialization completed in 1125 ms
TimGoodacre
Apr 26th, 2006, 03:41 AM
Interestingly if I create an additional empty context config (bob.xml) and set the web.xml like this:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:bob.xml</param-value>
</context-param>
<context-param>
<param-name>locatorFactorySelector</param-name>
<param-value>classpath:beanRefFactory.xml</param-value>
</context-param>
<context-param>
<param-name>parentContextKey</param-name>
<param-value>tradeserver-context</param-value>
</context-param>
It only loads the context once. So why then would it try to load the contextConfigLocation file as well as the locatorFactorySelector file? All I'm trying to do here is ensure that the spring context loaded by the ContextLoaderListener is the same one as I access from my class outside of the Spring container...
TimGoodacre
Apr 28th, 2006, 07:55 AM
:confused: So no-one has ever seen something like this? (or can spot my obvious mistake?)
Injecteer
Apr 28th, 2006, 08:25 AM
hi
try using the ContextSingletonBeanFactoryLocator in your glue code.
TimGoodacre
Apr 28th, 2006, 08:32 AM
hi
try using the ContextSingletonBeanFactoryLocator in your glue code.
Thanks Injecteer - have a look at the original post - my glue code is there and I am using ContextSingletonBeanFactoryLocator. It seems to work ok.
The problem is when I have both the contextConfigLocation and locatorFactorySelector configured to "classpath:beanRefFactory.xml" in the web.xml - it loads 2 Spring contexts...
Injecteer
Apr 28th, 2006, 08:41 AM
uups. my bad.
Actually you can remove locatorFactorySelector param, as I don't see how you can make use of it.
TimGoodacre
Apr 28th, 2006, 08:48 AM
uups. my bad.
Actually you can remove locatorFactorySelector param, as I don't see how you can make use of it.
That didn't work either (see my second post) :)
Injecteer
Apr 28th, 2006, 09:07 AM
try removing also:
<context-param>
<param-name>parentContextKey</param-name>
<param-value>tradeserver-context</param-value>
</context-param>
TimGoodacre
Apr 28th, 2006, 09:10 AM
try removing also:
<context-param>
<param-name>parentContextKey</param-name>
<param-value>tradeserver-context</param-value>
</context-param>
Thanks for your help Injecteer.
I thought the whole idea was to set that param and that provided the key for the glue code to use for lookup? Or will it automatically infer this from the contents of the beanRefFactory.xml file? :confused:
Sadly my understanding is from googling which hasn't been too succesful for this - doesn't seem to be any definitve doco on how this works...
Injecteer
Apr 28th, 2006, 09:22 AM
In my understanding, you must play with parent-params only if you have nested contexts.
Typically, the AppContext is initialized and used as a singleton, then the ContextLocator looks for that one without any additional keys. That's pretty well described in the chapter 3 of the docs with some basic but sufficient examples.
well, did your thing work? :)
TimGoodacre
Apr 28th, 2006, 09:38 AM
In my understanding, you must play with parent-params only if you have nested contexts.
Typically, the AppContext is initialized and used as a singleton, then the ContextLocator looks for that one without any additional keys. That's pretty well described in the chapter 3 of the docs with some basic but sufficient examples.
well, did your thing work? :)
Haven't tried it yet - have to write some actual business-related code at the moment ;) But I'm pretty sure I started out that way with no success...
sabarish
Apr 28th, 2006, 01:13 PM
Referring to the first post made...
The context is being loaded twice because the same file is being passed as contextConfigLocation and as the one to be used by loadParentContext(). I think the idea of a loadParentContext() (ie factory key and selector context params) was introduced to repeatedly get access to some beans (using a ContextSingletonFactory sometime later) that would refer to other beans defined in other context files.
Say for ex we had all service/manager/facade layer beans defined in service.xml and rest (ie dao, helpers etc) in implementation.xml. We also know that the web service proxy needs access to the service layer beans. Then we should reference service.xml in beanRefFactory.xml and pass implementation.xml as contextConfigLocation.
Then when the web service proxy uses ContextSingletonBeanFactoryLocator it should pass factory key and selector for the service.xml as defined in beanRefFactory.xml...
Hope iam right and this makes sense.
sabarish
Apr 28th, 2006, 01:47 PM
Referring to the first post made...
The context is being loaded twice because the same file is being passed as contextConfigLocation and as the one to be used by loadParentContext(). I think the idea of a loadParentContext() (ie factory key and selector context params) was introduced to repeatedly get access to some beans (using a ContextSingletonFactory sometime later) that would refer to other beans defined in other context files.
Say for ex we had all service/manager/facade layer beans defined in service.xml and rest (ie dao, helpers etc) in implementation.xml. We also know that the web service proxy needs access to the service layer beans. Then we should reference service.xml in beanRefFactory.xml and pass implementation.xml as contextConfigLocation.
Then when the web service proxy uses ContextSingletonBeanFactoryLocator it should pass factory key and selector for the service.xml as defined in beanRefFactory.xml...
Hope iam right and this makes sense.
Looks like iam wrong. The loadParentContext() should be used to load beans that are shared by other contexts and not the other way round. So all shared stuff (ie facade/service context _and_ context for beans that get injected into facade/service beans) can be specified in beanRefFactory.xml leaving only those that are dependent on these to be loaded using contextConfigLocation's context file.
One more approach to get to the existing factory loaded by contextloaderlistener would be to use WebApplicationContextUtils.
TimGoodacre
May 2nd, 2006, 02:28 AM
Sabarish,
Many thanks. I'd did try just specifying a parent context in the web.xml and then it also looked for the default application xml file which of course didn't exist. I need to have a play with this later today although I'm still very unclear on how this is meant to work.
T
TimGoodacre
May 10th, 2006, 11:43 AM
Ok I've finally had a chance to play with this some more. I still don't get it, nor do I see a clear explanation anywhere on how this should work (not in the Spring website doco either) :(
I have layed out the cases below that I have tried:
--------------------------------------------------
If I use this web.xml config the context loads twice - once from the listener and once from the bridging code:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beanRefFactory.xml</param-value>
</context-param>
--------------------------------------------------
If I use this web.xml config I get the error I have pasted below it:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beanRefFactory.xml</param-value>
</context-param>
<context-param>
<param-name>locatorFactorySelector</param-name>
<param-value>classpath:beanRefFactory.xml</param-value>
</context-param>
java.lang.IllegalArgumentException: Name must not be null
at org.springframework.util.Assert.notNull(Assert.jav a:116)
at org.springframework.beans.factory.BeanFactoryUtils .transformedBeanName(BeanFactoryUtils.java:58)
at org.springframework.beans.factory.support.Abstract BeanFactory.transformedBeanName(AbstractBeanFactor y.java:594)
--------------------------------------------------
When I use this config it loads the context twice, once from the listener and once from the bridging code:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beanRefFactory.xml</param-value>
</context-param>
<context-param>
<param-name>parentContextKey</param-name>
<param-value>tradeserver-context</param-value>
</context-param>
--------------------------------------------------
Anything leaving out the contextConfigLocation param causes the listener to try to load "applicationContext.xml" which of course does not exist.
--------------------------------------------------
Unhappily yours.
T
TimGoodacre
May 11th, 2006, 02:22 AM
One further case. I define an empty context file called "fixme.xml" and configure the web.xml like this:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:fixme.xml</param-value>
</context-param>
<context-param>
<param-name>locatorFactorySelector</param-name>
<param-value>classpath:beanRefFactory.xml</param-value>
</context-param>
<context-param>
<param-name>parentContextKey</param-name>
<param-value>tradeserver-context</param-value>
</context-param>
This loads the context once and shares it with bridging code. However it does not destroy the beans in the tradeserver.xml file when I shut the app down.
I can't but think that the whole SingletonAppContext business is not a very elegant solution within Spring when you are trying to share a context. It would be nice to just have the one normal context xml file and have an attribute on the beans element - singleton="true"... Then when the listener loads up the context and the bridging code loads up the App context with the same filename it would automatically share the objects.
I'd almost go so far as to say that this should be the default behaviour. Are there that many cases where you'd actually want more than one instance of your context in memory?
TimGoodacre
May 11th, 2006, 02:40 AM
And if I don't used the locator factory selector in the bridging code - e.g.
BeanFactoryLocator bfLocator = ContextSingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bfReference = bfLocator.useBeanFactory(CONTEXT_NAME);
...it loads the context a second time regardless.
TimGoodacre
May 11th, 2006, 05:06 AM
Some more variations... Really going round in circles here...
In web.xml for both cases I define:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beanRefContext.xml</param-value>
</context-param>
If I set the lazy=true attribute in the beanRefContext.xml it only loads the context once (when the bridging code calls it) but does not call any of the shutdown methods:
<beans>
<bean id="tradeserver-context" class="org.springframework.context.support.ClassPathXmlAp plicationContext" lazy-init="true">
<constructor-arg>
<list>
<value>tradeserverspring.xml</value>
</list>
</constructor-arg>
</bean>
</beans>
However if I don't have lazy=true, it loads the context once at startup and again in the bridging code. The shutdown hooks are called once (presumably for the non-bridge instantiated instances...
:( :( :(
TimGoodacre
May 11th, 2006, 06:56 AM
Okay, so a brief summary of what I know so far (in the hope that someone out there can relate):
You have to set the contextConfigLocation web.xml param. If you don't, it looks for applicationContext.xml
All beans loaded through the contextConfigLocation have their shutdown hooks called correctly
Bean instances loaded through the contextConfigLocation param are NOT accesible through the SingletonBeanFactory
If you specify a parentContextKey you must also specify a locatorFactorySelector
parentContextKey loaded beans are accessible through the ContextSingletonBeanFactoryLocator and are accessible from the bridging code
Beans aquired through the ContextSingletonBeanLocator (whether through the parentContextKey config or through the bridging code) DO NOT have shutdown hooks called
This all seems to make it pretty unusable for the basic case of sharing bean instances - particularly the last point. If the SingletonBeanFactory correctly called the shut-down hooks I could work around this behaviour by specifying an empty file in the contextConfigLocation.
Can anyone confirm this behaviour?
It looks like I might have to abandon the bridging code and have the shared objects keep a static reference to themselves when instantiated so that the bridging code can access that instead :( Or alternatively write my own Singleton manager that the shared objects register themselves with.
Not good.
T
TimGoodacre
May 16th, 2006, 07:42 AM
:confused: Seriously? No-one can re-create this? Or no-one thinks it is a problem?
T
TimGoodacre
May 19th, 2006, 02:17 AM
:confused: You shouldn't need Tomcat to reproduce this... Looking at the Spring source code it is the use of the Factory that is causing the beans not to have their shutdown hooks called...
Powered by vBulletin® Version 4.2.1 Copyright © 2013 vBulletin Solutions, Inc. All rights reserved.