Using a shared parent application context in a multi-war Spring application

Last month I gave a Core Spring training in Turkey. At the end of the course I discussed the architecture for an application that some of the participants were going to build after completing the course. This application would consist of an ear file with several war files inside, and the question came up if it was possible to define a single ApplicationContext that could be used as a shared parent to the WebApplicationContexts of all war files. This context would hold bean definitions for services, dao's and other beans that were not specific to a single web module.
Actually, Spring makes it very easy to do this, but neither the course nor the reference manual explain in detail how to use this feature from your web application. I therefore wrote a short sample app that illustrates how this works, which I will discuss here in my first blog entry.
The ContextLoader and SingletonBeanFactoryLocator classes
In a typical Spring web application, you use a ContextLoaderListener (or, if you're using a Servlet 2.2 / 2.3 container, a ContextLoaderServlet) to bootstrap a WebApplicationContext. You configure the ContextLoader used by this class through context parameters in your web.xml. If you've used this, you're probably familiar with the contextConfigLocation parameter, that let's you specify which files make up the WebApplicationContext to construct.
As it turns out, there is another parameter that you can use to get the desired functionality in a declaritive fashion: parentContextKey. Using this, you instruct the ContextLoader to use another class called ContextSingletonBeanFactoryLocator to search for a bean named by the value of parentContextKey, defined in a configuration file whose name matches a certain pattern. By default, this pattern is 'classpath*:beanRefContext.xml', meaning all files called beanRefContext on the classpath. (For a plain SingletonBeanFactoryLocator it's 'classpath*:beanRefFactory.xml')
This bean must be an ApplicationContext itself, and this context will become the parent context for the WebApplicationContext created by the ContextLoader. However, if this context already exists then that one will be used and no new context will be created (hence the name SingletonBeanFactoryLocator).
Let's see what this means: first, we need a separate jar in our ear that holds the code for the services, DAO's, etc. Inside this jar we place a beanRefContext.xml file that holds a single bean-definition for an ApplicationContext. Typically, this will be a ClassPathXmlApplicationContext. Then that bean definition will refer to one or more 'regular' bean configuration files that contain the service beans and other stuff to be used by the code from your war files; something like this:
notice that the bean id is the value specified by the parentContextKey param –>
<bean id="ear.context" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>services-context.xml</value>
</list>
</constructor-arg>
</bean>
Finally, we need to make this jar available on the classpath of the war files using the Class-Path entry in the MANIFEST.MF file of each war.
Why would you want to use this?
A simpler solution is to skip the parentContextKey and to load the shared bean definition files from each war using the contextConfigLocation parameter. This way, each war will have its own instance of each shared bean. For simple stateless beans, such as typical services, this is actually a fine solution.
However, instantiating a new instance of each shared bean for each war can have several drawbacks: a common example is creating a Hibernate SessionFactory. This is often an expensive process, so to prevent your startup time getting out of hand the described solution will ensure that this is done only once. Another advantage of having a single instance of your SessionFactory is that it can safely act as a second level cache: you don't want multiple copies of that hanging around in a single application!
In general, if you have stateful beans that should really be used as a singleton (in the Spring sense, i.e. one instance per application as opposed to per JVM) you should define them in a single context accessed by the other contexts.
The sample
I've included the sample, both as an ear file and as source. In order to upload the ear, I had to give it a .zip extension: please rename the file to .ear before deploying it!. The source is actually an Eclipse workspace, so you can easily import and review it (it requires WTP and is configured for Spring IDE). All Spring jars needed are included. After you've deployed the app, go to the URLs /web1 and /web2 to see the output of a servlet in the first and the second war file. The toString() of the service will prove that the wars really use the same instance of the shared service.
More info
The best info on this feature is in Spring's excellent API documentation: have a look at the JavaDoc for the ContextLoader.loadParentContext method and the SingletonBeanFactoryLocator class. These docs contain further info on how to configure your web.xml and how to write the beanRefFactory.xml.
Modified

Erwin Vervaet says:
Added on June 12th, 2007 at 12:48 am -QuoteNice entry. I wondered about this, but never actually looked it up.
Christian Seiler says:
Added on June 12th, 2007 at 6:49 am -QuoteI'm wondering how session-scoped beans would fit into this scenario. Since the shared context is not a web context, I assume that you can't use session-scoped beans there. Right?
Joris Kuipers (blog author) says:
Added on June 12th, 2007 at 7:43 am -QuoteOn June 12, 2007 at 6:49 am, Christian Seiler said:
Right; even if you would make the shared context a WebApplicationContext somehow, it still wouldn't make any sense: the Session is scoped to the ServletContext (which corresponds to a war file), so I'd say that session scope is meaningless outside the web application context of the war itself.
That doesn't prevent you to place shared session-scoped bean definitions inside a single configuration file, of course: it only means that this file should be used to construct the WebApplicationContexts of each war by adding it to the list of files in the contextConfigLocation parameter in each war's web.xml. Still DRY, but these beans will not become part of the shared parent context.
Corby Page says:
Added on June 12th, 2007 at 8:58 am -Quote"Instantiating a new instance of each shared bean for each war can have several drawbacks…"
Maybe so, but it also has one distinct advantage: predicatable, repeatable behaviour.
Maybe the implementation of ContextSingletonBeanFactoryLocator has been overhauled in Spring 2.0, but if not, I would strongly discourage its use unless you really know what you are doing.
When I used ContextSingletonBeanFactoryLocator, it operated very much at the mercy of the Classloader implementation of my server environment. When the classloader implementation was flaky, you ended up with singletons-that-are-not-really-singletons. For stateful components, this means that predicting the result of a method call has all the surprise and adventure of unwrapping a present on Christmas morning.
Joris Kuipers (blog author) says:
Added on June 12th, 2007 at 9:26 am -QuoteAbsolutely, but with flaky classloaders that would also be the case if you implemented your own singleton, right?
In the setup that I described (multiple wars and a jar in the root of the ear, referenced from the Class-Path entry in the MANIFEST.MF as opposed to placed in WEB-INF/lib), most if not all containers will by default create a ClassLoader per war and a parent ClassLoader for the jar(s). This ensures that the wars can't see each other's classes and that the jar can't see the wars. Thus, the singleton beans will be loaded by that parent CL. I've never experienced problems with this setup myself, but I can imagine other scenario's, like using the (Context)SingletonBeanFactoryLocator programatically without having a parent CL in place, that can lead to the problems you descibe (there's actually a warning in the JavaDoc of the BeanFactoryLocator about this sort of usage).
So I agree that a SingletonBeanFactoryLocator should only be used (a) if really needed and (b) if you understand how it interacts with your server environment. Then again, I feel the same about most technologies
Benoit Orihuela says:
Added on June 13th, 2007 at 2:30 am -QuoteVery, very interesting article. In fact, that's exactely what I was looking for for months ! And, as always with Spring, there's a simple and staightforward solution
Thanks !
Benoit.
Kerry Wright says:
Added on June 15th, 2007 at 12:48 pm -QuoteAlso good to make clear, the ContextLoader uses the ContextSingletonBeanFactoryLocator not the SingletonBeanFactoryLocator and in the JavaDoc for the former, it specifically says: "It is not possible nor legal to share definitions with SingletonBeanFactoryLocator". So if other components of your system are using that Locator (for instance a JbossAcegiLoginModule) you might end up creating 2 copies of every bean anyways.
Jörg Heinicke says:
Added on June 28th, 2007 at 3:50 am -QuoteIf you are not in an EJB container, but only use a servlet container, you have to deploy your classes for the global application context also into a global class loader, don't you? This gets a lot of stuff if you think of the domain model which you need for the mentioned SessionFactory.
My use case would be a portal web app a portlet web app which I have running both on plain Tomcat only, but I need to integrate them somehow: http://www.liferay.com/web/guest/devzone/forums/message_boards/message/35365. With the global context I'd need to deploy everything of the portlet web app except the MVC stuff to Tomcat's common class loader (common/lib, http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html). At some point you maybe have to switch to an EJB container - and if it's only for the class loader/ deployment stuff.
Joerg
Burak says:
Added on July 2nd, 2007 at 8:02 am -QuoteHi Joris,
Did you remember me? I'm one of guys who gave Spring course in Turkey… Thanks for your reply. We're gonna use the solution you suggest.
Burak ARIK
Joris Kuipers (blog author) says:
Added on July 2nd, 2007 at 3:18 pm -QuoteOn July 2, 2007 at 8:02 am, Burak said:
Hi,
yes, sure I remember you. I hope you'll have a successful project and that you can apply what you've learned!
Joris
Rapthor says:
Added on July 5th, 2007 at 4:54 am -QuoteHello,
nice approach to share a context across multiple web apps. One problem occurs for me: If I put jars into my EAR file and insert them into the MANIFEST.MF they are not found.
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.6.5
Created-By: 1.5.0_11-b03 (Sun Microsystems Inc.)
Class-Path: ojdbc14.jar
For me it is "ClassNotFoundException: oracle.jdbc.driver.OracleDriver" when initializing the EAR parent application context. Is there a possibility to tell Spring to use the classpath as mentioned in MANIFEST.MF?
Rapthor says:
Added on July 5th, 2007 at 6:51 am -QuoteI think I misunderstood something about your approach. What I did was putting my shared classes into a seperate jar file and provide it via Tomcat's shared lib folder. Then I assumed another webapp should have access to the jar and load its context as parent. While the parent context was initialized, I got the ClassNotFoundExceptions as described above.
Is there no way to do it the way I described here? I do not want to use an application server hence I do want to avoid EAR-files.
Joris Kuipers (blog author) says:
Added on July 6th, 2007 at 3:27 am -QuoteOn July 5, 2007 at 6:51 am, Rapthor said:
You have indeed misunderstood my original description, since it only descibes a solution for ear-files with multiple war-files inside.
However, what you want is certainly possible: I've just tried it out, and it works perfectly. Just make sure that not only the shared parent jar is placed inside the shared/lib directory, but also the the spring jar that holds the ContextSingletonBeanFactoryLocator class (and other jars that your application might need). This way, there can be an actual singleton in the shared classloader visible to both web applications that each have their own classloader.
This should work using my original example:
- download the multiple-contexts-sample.zip linked to from the blog entry
- put all jars from that zip in Tomcat's shared/lib folder
- (re)start Tomcar
- deploy the 2 war-files from that zip under /web1 and /web2
- go to http://localhost:8080/web1 and …/web2 and ensure that the same instance of the shared service is now used (refresh a couple of times to see the shared counter being incremented)
I guess this also answers Jörg's question. BTW, don't use the common dir, use the shared dir: the common is for libraries that should also be seen by Tomcat itself, these jars are loaded by a parent classloader of the shared classloader. The classes in the shared classloader are only visible to deployed applications, which is exactly what we want in this case.
ziki says:
Added on August 24th, 2007 at 9:57 am -QuoteI put the all spring applicaitonContext.xml to myapp.ear/APP-INF/classes
Dimitri Hautot says:
Added on September 5th, 2007 at 10:41 am -QuoteHello,
I deployed the .ear file on WebLogic server 9.2, and the sample code doesn't work: each Webapp has its own instance of the service bean.
Is this problem known with other application servers?
Thanks,
Dimitri
Joris Kuipers (blog author) says:
Added on September 5th, 2007 at 12:26 pm -QuoteOn September 5, 2007 at 10:41 am, Dimitri Hautot said:
That's because WebLogic by default doesn't put regular jar files from the root of the ear in a parent classloader like with EJB jars, but in the classloader of the WARs. See http://edocs.bea.com/wls/docs92/programming/classloading.html for more info and possible solutions. In your case, moving al jars from the root of the ear in /APP-INF/lib seems like the easiest solution.
Dimitri Hautot says:
Added on September 7th, 2007 at 3:44 am -QuoteHello,
Indeed, moving all utility .jar files from the root to /APP-INF/lib is the solution.
So, not only the SampleJava.jar file, but also spring-*.jar.
Thank you for the tip!
Ales Novy says:
Added on September 19th, 2007 at 10:27 am -QuoteUnfortunately, similar mechanism is missing for EJBs in current ContextJndiBeanFactoryLocator implementation. It would be nice if it can take also "parentContextKey" parameter similar to org.springframework.web.context.ContextLoaderListener to have one shared root context among EJB JARs as well as WARs.
Jee says:
Added on September 25th, 2007 at 7:42 am -QuoteHi People,
Can anyone advise me on the following issue. I trying to define seperate context names using the same .war file.
For example: I have a war file abc.war and I want to configure it so that it can be accessed using http://localhost/abc1 and http://localhost/abc2. Is it possible with tomcat and if so what will I have to do to achieve it?
Thank you.
-jee
Joris Kuipers (blog author) says:
Added on September 25th, 2007 at 8:06 am -QuoteThis blog entry has nothing to do with that. Please use this page only for relevant comments or questions. For non-related questions, consult a forum or read some documentation. You could start here: http://en.wikipedia.org/wiki/Rewrite_engine
Jee says:
Added on September 25th, 2007 at 10:36 am -QuoteIndeed Joris, this is not related. But I thought it is some what related and after reading this block I felt it could be valuable to get your input on that.
JoeC says:
Added on October 22nd, 2007 at 10:42 am -QuoteThanks for the great article and example. It works very well.
One problem we are having is that we dynamically load our application context, selecting the configuration files in Java. This also works well, but we how can we connect the two approaches? We created a custom listener and loader for a web application that creates and loads the context. As a result, it is not defined as a Spring bean in a configuration file. We would now like our dynamically created context to become the parent context for other web modules. Is there some way that we can "name" this context so that we can reference it in the web.xml of other web modules? I searched for a solution, but didn't find anything. Here is our custom ContextLoader code:
public class JeeContextLoader extends ContextLoader {
protected ApplicationContext loadParentContext(ServletContext servletContext) throws BeansException {
String moduleReferences = servletContext.getInitParameter("ModuleReferences") ;
if ( moduleReferences == null ) {
System.out.println("ModuleReferences is null");
} else {
System.out.println("ModuleReferences:" moduleReferences);
}
BootStrapper bootStrapper = new BootStrapper() ;
bootStrapper.setModuleReferences(moduleReferences);
Globals.Init(bootStrapper, FactoryInitAction.StartFactory);
ApplicationContext parentContext = Globals.getFactory() ;
return parentContext;}}
Joris Kuipers (blog author) says:
Added on October 22nd, 2007 at 1:57 pm -QuoteOn October 22, 2007 at 10:42 am, JoeC said:
Since you've already created your own ContextLoader and have overridden the loadParentContext method, why would you still want to name that root context? That name is normally only used by the default loadParentContext method, since it delegates to a ContextSingletonBeanFactoryLocator and looks up the context named by the parentContextKey parameter. You're not using that in your ContextLoader implementation…
In your case, I'd say that all you need is to instruct the ContextLoaderListener to use your JeeContextLoader instead of the default ContextLoader. To do that, all you need to do is to subclass ContextLoaderListener, override createContextLoader to create a JeeContextLoader and register that subclass as a listener in your web.xml. You mentioned that you have also created a custom listener already, so I'm not sure if I fully understand your problem: if your code always returns the same ApplicationContext when the same ModuleReferences value is passed to it, then it looks like you have already created a working solution… The code that does this isn't too clear, however.
JoeC says:
Added on October 22nd, 2007 at 4:26 pm -QuoteThanks Joris, for your help. We are basically trying to do what you have done in your sample application without defining the application context in a Spring xml configuration file. We are doing this to support dynamic configuration for testing, shared Acegi security accross WARs, etc. Our same bootstrapper works for everything.
You were right in that we already had a solution. I just needed to add the functionality in our custom loader to point to a parent context, and it worked. I copied the following code right out of the Spring context loader.
BeanFactoryLocator bfLocator = ContextSingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bfReference = bfLocator.useBeanFactory(parentContextKey);
parentContext = (ApplicationContext) bfReference.getFactory();
JoeC says:
Added on October 23rd, 2007 at 8:37 am -QuoteActually, this did not solve my problem. I apologize for the premature response. I still can't get my programmatically created ApplicationContext to have a beanId that I can reference (as a parentContextKey) from other parts of my EAR.
Joris Kuipers (blog author) says:
Added on October 23rd, 2007 at 1:34 pm -QuoteOn October 23, 2007 at 8:37 am, JoeC said:
That's not how it works: if you use your JeeContextLoader, it will just ignore the parentContextKey since you've overridden the loadParentContext method to create the parent context in a different way. I didn't understand your previous post, but given that it doesn't work that probably makes sense
What you need to do is to create a singleton (in the GoF sense) that holds the parent ApplicationContext that you then return from the loadParentContext method. Also, make sure that all your WAR files use your JeeContextLoader and not the default ContextLoader.
JoeC says:
Added on October 25th, 2007 at 3:23 pm -QuoteThank you! This worked perfectly.
Arnon Davidovici says:
Added on November 2nd, 2007 at 7:34 pm -QuoteI have a client with the following issue…. They have a baseline non-branded WAR that works for most of their clients. However some clients (lets call them partners) are more demanding and would like something more configurable. My idea is to have a thin set of overriding/extension code for just the changes that are necessary for each partner. That partner override code will be in a separate WAR so that it can be tested and deployed separately. By using your solution I can have the child context replace the bean definitions for the parent context thereby injecting the partner code instead of the baseline code with no effect on the baseline. Is this correct? Is this the most effective way to gain this flexibility? Thanks.
Joris Kuipers (blog author) says:
Added on November 3rd, 2007 at 5:17 am -QuoteDo you mean that the partners will use both the base WAR and a seperate WAR, or that the seperate WAR will contain the base plus overriding/extension code? In the last case, you don\'t need multiple contexts: you can simply override base bean definitions by adding extra configuration files to the list of files that make up the application context. Wildcarding would work here probably, maybe something like this as the context-param value in your web.xml:
I didn\'t try this, but it seems like this would enable you to simply put the extra code under /WEB-INF/lib or /WEB-INF/classes in the base WAR and create one or more extra configuration files called something-override.xml that you also place on the classpath, which can then override existing base bean definitions.
If you meant using multiple WAR files then I\'m not really getting your deployment scheme here: how would deploying an extra WAR file change the behavior of an already deployed base WAR file? My sample only talks about a shared parent context for multiple WAR files in a single EAR file: the base WAR file presumably doesn\'t use a parent context for the context created by the ContextLoaderListener.
Arnon Davidovici says:
Added on November 6th, 2007 at 12:02 pm -QuoteThanks for the quick response. Sorry it took a few days to get back. You actually bring up a separate point. I am trying to keep the WARs separate, but I don't know if I can.
Option 1: Keeping the WARs separate… This may not be possible because they are using Struts with JSP and Velocity. I am in the process of researching online if those files will be accessible in a separate WAR. In this case, I believe you are saying the parent context is the way to go.
Option 2: Merging the baseline and override… So are you saying that if the override defines a bean later in the initialization than the baseline it will replace the bean that was originally defined with that id? If so, that's much easier, but I couldn't see that documented anywhere. For some reason, I was thinking it might output a BeanAlreadyDefinedException (or something of the sort).
Joris Kuipers (blog author) says:
Added on November 6th, 2007 at 12:16 pm -QuoteOn November 6, 2007 at 12:02 pm, Arnon Davidovici said:
Yes, Spring has no problem with multiple bean definitions with the same id, as long as they're not in the same file. This definitely seems like the easiest thing to do, and is supported just for this use case. The latest bean definition that's loaded simply 'wins' and masks other definitions with the same id.
Aidan Whiteley says:
Added on December 2nd, 2007 at 12:34 pm -QuoteHi,
many thanks for the very useful article. Like Dimitri Hautot, I've been trying this on WebLogic 9 and it works well. There's a slight difference in what I'm doing in that I want a bean at the managed server (i.e. JVM) level but that works fine with the bean code and required Spring jars at the server level (versus app-inf/lib at the ear level).
However, most of my web apps run with the WebLogic prefer-web-inf-classes setting changed to true (i.e. look first for code in the web app's web-inf/lib rather than first looking in the higher classloaders). When I turn this on, I get two instances of the SampleService class. I _think_ I understand why - the ContextSingletonBeanFactoryLocator is running from the web app's classloader and, although the SimpleService class is created by the server level classloader (it is not deployed to web-inf/lib), the ContextSingletonBeanFactoryLocator in each web app know nothing about the ApplicationContexts each other have created.
Does the above sound right? If so, do you know if there is an easy way round this i.e. so I can continue to run with prefer-web-inf-classes but am still able to pull in a single instance of an ApplicationContext created in a higher classloader as a parent into different web app's ApplicationContexts.
Thanks in advance if you have any pointers. I realise what I'm trying to do might sound strange i.e. turn on prefer-web-inf-classes to increase segregation between web apps while then wanting to also also put something on the server classpath but I would claim to have a valid use case for this!
Joris Kuipers (blog author) says:
Added on December 2nd, 2007 at 4:02 pm -QuoteOn December 2, 2007 at 12:34 pm, Aidan Whiteley said:
I'd say that you only have to move the spring.jar out of WEB-INF/lib: if you want to share the ApplicationContext through a singleton, then that's required. The rest of your code can probably stay in WEB-INF/lib. The reason that I say 'probably' is that code that's in a higher classloader that needs to access classes loaded by the web classloader will need to properly use the context classloader in that case.
Maybe you should go for another solution instead, for instance registering your shared ApplicationContext in the JNDI registry and perform a lookup from the WAR. You'd need to ensure that the shared context would have been created and registered before the lookup occurs, of course.
Aidan Whiteley says:
Added on December 3rd, 2007 at 4:27 pm -QuoteHi Joris,
thanks for the ultra speedy response! I am a little reluctant to change the existing web apps to move out the spring jar to the higher classloader. So I guess I'll need to think about alternative solutions - sharing an application context via JNDI sounds like fun to try!
Thanks again - appreciated.
Derek says:
Added on December 4th, 2007 at 3:10 pm -QuoteJust a note to everyone out there who is using Weblogic, ehcache (in a parent context) and multiple wars:
There are some problems with parent context distributed caching in Weblogic (see the below stacktrace). To get around this, make sure you have the ehcache included in your web application's pom file/classpath (as opposed to the ear classpath) so that it can be referenced by the classloader.
2007-12-04 09:12:16,549 WARN - Unable to send message to remote peer. Message was: RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: net.sf.ehcache.distribution.EventMessage (no security manager: RMI class loader disabled)
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: net.sf.ehcache.distribution.EventMessage (no security manager: RMI class loader disabled)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:325)
at sun.rmi.transport.Transport$1.run(Transport.java:153)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:149)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:466)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:707)
at java.lang.Thread.run(Thread.java:595)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:247)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:223)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:126)
at net.sf.ehcache.distribution.RMICachePeer_Stub.send(Unknown Source)
at net.sf.ehcache.distribution.RMIAsynchronousCacheReplicator.flushReplicationQueue(RMIAsynchronousCacheReplicator.java:313)
at net.sf.ehcache.distribution.RMIAsynchronousCacheReplicator.replicationThreadMain(RMIAsynchronousCacheReplicator.java:122)
at net.sf.ehcache.distribution.RMIAsynchronousCacheReplicator.access$100(RMIAsynchronousCacheReplicator.java:55)
at net.sf.ehcache.distribution.RMIAsynchronousCacheReplicator$ReplicationThread.run(RMIAsynchronousCacheReplicator.java:372)
Caused by: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: net.sf.ehcache.distribution.EventMessage (no security manager: RMI class loader disabled)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:285)
at sun.rmi.transport.Transport$1.run(Transport.java:153)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:149)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:466)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:707)
at java.lang.Thread.run(Thread.java:595)
Caused by: java.lang.ClassNotFoundException: net.sf.ehcache.distribution.EventMessage (no security manager: RMI class loader disabled)
at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:371)
at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:165)
at java.rmi.server.RMIClassLoader$2.loadClass(RMIClassLoader.java:620)
at java.rmi.server.RMIClassLoader.loadClass(RMIClassLoader.java:247)
at sun.rmi.server.MarshalInputStream.resolveClass(MarshalInputStream.java:197)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1544)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1466)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1699)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1305)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:348)
at java.util.ArrayList.readObject(ArrayList.java:591)
at sun.reflect.GeneratedMethodAccessor3.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:946)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1809)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1719)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1305)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:348)
at sun.rmi.server.UnicastRef.unmarshalValue(UnicastRef.java:290)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:279)
… 6 more
EDH says:
Added on January 5th, 2008 at 8:48 am -QuoteJoris,
I'm using the shared context approach not for sharing my context between wars but between my war module and my ejb module.
More specifically I have one EAR with one ejb-jar module and one war module.
My EJBs (MDB's and SLSBs) extend from Spring's convenience classes and call as described in multiple threads on the web the setBeanFactoryLocator(ContextSingletonBeanFactoryLocator.getInstance("classpath*:**/beanRefContext.xml")); and setBeanFactoryLocatorKey("service"); methods.
In my web.xml I have the correspondig parentContextKey and the locatorFactorySelector entries.
Everything works greath … except in one scenario … when I deploy/(re)start my EAR when there are messages on the queue. In this situation I'm running into the problem that the container preloads the MDB and calls the setMessageDrivenContext method in which the above method calls occur. Of course, at that point in time my web module is not started yet and therefore the shared application context is not loaded/initialized yet.
As a result I get a spring exception and my application doesn't start.
Is there a way to get around this problem ? For example, can I initialize the context from within my ejb and "make it available" to my web module, or could deploying a spring context as a JCA adapter help ?
Any help, advise, workarounds would be appreciated because I'm really stuck.
Kind regards,
EDH
Joris Kuipers (blog author) says:
Added on January 5th, 2008 at 10:06 am -QuoteOn January 5, 2008 at 8:48 am, EDH said:
I don't think the shared context can only be created by the ContextLoader from the web module; that class has a method called loadParentContext which calls BeanFactoryLocator.useBeanFactory(parentContextKey) which triggers the creation of the shared parent context, but the AbstractEnterpriseBean.loadBeanFactory method does exactly the same thing. That method gets called from AbstractMessageDrivenBean.ejbCreate(): that should happen after the setMessageDrivenContext method gets called by the container, so the loadBeanFactory should recreate your parent context using your ContextSingletonBeanFactoryLocator and beanFactoryLocatorKey, right? So, just by looking at the code, one would expect the shared parent context to be loaded and initialized just fine, regardless of the module that starts first.
Can you provide the exception that you receive? This might have something to do with the shared context not being properly destroyed on restart, so it doesn't get recreated when useBeanFactory gets called. It might also help to put in some trace logging in the methods involved to see what happens exactly after a restart. Also enable trace logging for the org.springframework.beans.factory loggers to see what Spring is doing!
HTH,
Joris
EDH says:
Added on January 6th, 2008 at 8:27 am -QuoteJoris,
Thanks for your quick response !
In the meantime I also created a bug report for this: http://jira.springframework.org/browse/SPR-4301. That bug report shows the exceptions I'm getting. Now, I might have been a bit to fast to consider this a bug. From the comments on my bug report you'll see that a first problem I'll have to solve is to find out why the spring.schemas (file) is not found when the MDB gets created(preloaded) while in the normal scenario, when the web module is first loaded the schemas are found without any problem ? Do you have some advise on this perhaps ?
Now, let's say I've solved this schema problem then what I understand from your comment is that when the MDB is created/preloaded (before the web module has started and thus before the web module's contextloader kicks in) the setBeanFactoryLocator(ContextSingletonBeanFactoryLocator.getInstance("classpath*:**/beanRefContext.xml")) call will also lead to the creation/initialization of the shared context. Is that correct ? Is so, then what happens at the time the web module gets loaded and it's contextloader kicks in ? Will it "detect" that the shared context is already there, or will it try to re-initialize it again ?
In either case, I'll try out your suggestions, and get back to you on this.
Thanks,
EDH
Joris Kuipers (blog author) says:
Added on January 6th, 2008 at 10:53 am -QuoteOn January 6, 2008 at 8:27 am, EDH said:
Not sure: could be classloader-related, maybe? Where is your spring.jar and how do you make it available to the ejb jar? If the spring.jar is in the ear, is it also refered to in the MANIFEST.MF Class-Path entry of the ejb jar?
On January 6, 2008 at 8:27 am, EDH said:
Almost: not the setBeanFactoryLocator call leads to creation, but the call to loadBeanFactory from the ejbCreate.
On January 6, 2008 at 8:27 am, EDH said:
It should detect it, since the context is already registered in a Map that's kept as a singleton (so your web module and your ejb module should use a spring.jar loaded by the same single classloader).
Joris
Eric says:
Added on June 19th, 2008 at 11:04 am -QuoteIt looks like the default file isn't actually beanRefFactory.xml, but beanRefContext.xml. That confused me for sometime, until I looked at the source download.