Announcement Announcement Module
Collapse
No announcement yet.
ApplicationContext hierarchy loaded at application startup Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • ApplicationContext hierarchy loaded at application startup

    Hi all,

    We have a web application with several jar modules. Each module has it's own context. We wanted those context to be loaded automatically at application startup to form a context hierarchy. Looking at the docs, I didn't see anything that could do this out of the box. Searching through this forum, I found a couple of posts related to this same problem, among others:

    http://forum.springframework.org/showthread.php?t=10228
    and
    http://forum.springframework.org/showthread.php?t=10335

    So I started from these posts and looked at the javadocs to find out that everything was there in the Spring APIs to load the context hierarchy at application startup. All that I needed to do was to create my own ContextLoader class and a ContextLoaderListener that instantiated this custom ContextLoader. My custom ContextLoader overrides loadParentContext() to load the hierarchy, using SingletonBeanFactoryLocator.

    I would like to know if this is the way it should be done? Or if anything already exists in Spring for this matter? Here is the source code. Hope it's usefull for other Spring users!

    Source for context loader:
    Code:
    package com.pyxis.spring.web.context;
    
    import javax.servlet.ServletContext;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.access.BeanFactoryLocator;
    import org.springframework.beans.factory.access.BeanFactoryReference;
    import org.springframework.beans.factory.access.SingletonBeanFactoryLocator;
    import org.springframework.context.ApplicationContext;
    import org.springframework.web.context.ContextLoader;
    
    /**
     * Context loader class that loads a parent context, using 
     * <code>SingletonBeanFactoryLocator</code>. Uses 
     * <code>SingletonBeanFactoryLocator</code> default definition 
     * (beanRefFactory.xml) file as the default parent context definition file. It
     * is possible to override this definition by adding the 
     * <i>parentContextConfigLocation</i> context-parame in the web.xml file. 
     * 
     * @author 	E. Hardy
     * @version $Revision: $ $Date: $
     */
    public class ContextHierarchyLoader 
    		extends ContextLoader
    {
        /**
         * Name of servlet context parameter that can specify the parent config 
         * location for the root context, falling back to the implementation's 
         * default else (beanRefFactory.xml).
         * 
         * <p>This constant value is: parentContextConfigLocation 
         */
        public static final String PARENT_CONFIG_LOCATION_PARAM = 
            	"parentContextConfigLocation";
        
        /**
         * Name of servlet context parameter that can specify the parent bean factory 
         * name for the root context, falling back to the implementation's 
         * default else (parentBeanFactory).
         * 
         * <p>This constant value is: parentBeanFactoryName
         */
        public static final String BEAN_FACTORY_NAME_PARAM = "parentBeanFactoryName";
        
        private static final String DEFAULT_BEAN_FACTORY_NAME = "parentBeanFactory";
        
        protected ApplicationContext loadParentContext(ServletContext servletContext)
                throws BeansException
        {
            String parentContextConfig = servletContext.getInitParameter(
                    		PARENT_CONFIG_LOCATION_PARAM);
            BeanFactoryLocator locator = null;
            
            if ((parentContextConfig != null) 
                    && (parentContextConfig.trim().length() > 0))
            {
                locator = SingletonBeanFactoryLocator.getInstance(parentContextConfig);
            }
            else
            {            
                locator = SingletonBeanFactoryLocator.getInstance();
            }
            
            String beanFactoryName = servletContext.getInitParameter(
                    		BEAN_FACTORY_NAME_PARAM);
            BeanFactoryReference bfr = null;
            
            if ((beanFactoryName != null) 
                    && (beanFactoryName.trim().length() >0))
            {
                bfr = locator.useBeanFactory(beanFactoryName);
            }
            else
            {
                bfr = locator.useBeanFactory(DEFAULT_BEAN_FACTORY_NAME);
            }        
            
            return (ApplicationContext)bfr.getFactory();
        }
    }
    And the source for the context loader listener:
    Code:
    package com.pyxis.spring.web.context;
    
    import org.springframework.web.context.ContextLoader;
    import org.springframework.web.context.ContextLoaderListener;
    
    /**
     * Bootstrap listener to start up Spring's root 
     * <code>WebApplicationContext</code>. This listener delegates to 
     * <code>ContextHierarchyLoader</code> to bootstrap the context hierarchy.
     *  
     * @author 	E. Hardy
     * @version $Revision: $ $Date: $
     */
    public class ContextHierarchyLoaderListener 
    		extends ContextLoaderListener
    {
        protected ContextLoader createContextLoader()
        {
            return new ContextHierarchyLoader();
        }
    }
    Cheers,
    Last edited by robyn; May 14th, 2006, 10:45 AM.

  • #2
    Yes, you're doing it the right way. I've actually posted code to the mailing list a few times that is basically almost identical to yours, and the ejbtest integration test/sample contains a version:

    http://cvs.sourceforge.net/viewcvs.p....2&view=markup

    Comment


    • #3
      Thanks for the reply! Any chance will see that piece of code integrated in the next Spring release?

      Cheers,

      Comment


      • #4
        You should check out Matt Raible's solution to this problem. It's much simpler and uses no custom code:

        http://www.jroller.com/page/raible?a...g_config_files

        Comment


        • #5
          Originally posted by cardsharp
          You should check out Matt Raible's solution to this problem. It's much simpler and uses no custom code:

          http://www.jroller.com/page/raible?a...g_config_files
          What Matt's post is showing is not a hierarchy of contexts though. It's just relying on the standard spring classpath*: prefix to load all fragments of the same name. That's great if this is good enough, but if you need a hierarchy that's something different...

          Comment


          • #6
            As far as I can tell using classpath: in contextConfigLocation param like Matt Raible shows does not support the classpath*: directive or wildcard characters like applicationContext*.xml and does not allow you to use a beanRefFactory.xml. All of which I need.

            The solution proposed by E. Hardy is exactly what I am looking for. I had a similar implementation before I came across this post, but I could not get it to work. I tried the exact code posted and that doesn't work either. I get the following error while tomcat 5.5.9 is starting up:

            [java] SEVERE: Exception sending context initialized event to listener inst
            ance of class com.rjlg.commons.frmwrk.spring.SpringHelperContext LoaderListener
            [java] org.springframework.beans.FatalBeanException: Unable to load group d
            efinition. Group resource name [classpath:conf/spring/beanRefFactory.xml], facto
            ry key [beanFactory]; nested exception is org.springframework.beans.factory.Bean
            DefinitionStoreException: IOException parsing XML document from class path resou
            rce [conf/spring/beanRefFactory.xml]; nested exception is java.io.FileNotFoundEx
            ception: Could not open class path resource [conf/spring/beanRefFactory.xml]
            [java] org.springframework.beans.factory.BeanDefinitionSt oreException: IOEx
            ception parsing XML document from class path resource [conf/spring/beanRefFactor
            y.xml]; nested exception is java.io.FileNotFoundException: Could not open class
            path resource [conf/spring/beanRefFactory.xml]
            [java] java.io.FileNotFoundException: Could not open class path resource [c
            onf/spring/beanRefFactory.xml]


            There is a jar in WEB-INF/lib that has a conf/spring/beanRefFactory.xml. I can get the same code working in a standalone application.

            I'm using Spring 1.1.4.

            Comment


            • #7
              Actually, it works great. I have lots of problems getting log4j to initialize correctly when I redeploy to tomcat. It happened to initialize correctly on a deploy and I saw lots of spring log info that showed it was finding the conf/spring/beanRefFactory.xml. I was getting an error later on because I had removed the configContextLocation param from web.xml, so it was looking for /WEB-INF/applicationContext.xml by default and did not find one when it was doing the context initialization of the WebApplicationConext. You have to remember that this solution does not take the place of the WebApplicationConext initialization, it initializes a parent context of the WebApplicationContext using a beanRefFactory. Very nice.

              It has my vote for inclusion into the framework! It is very helpful for anyone that builds both web and standalone clients and uses shares spring enabled jars between them. I wrote a SpringHelper class that acts as a facade for spring initialization for my standalong clients. So now for the web apps I just make the custom ContextLoader delegate to it, so I'm use the same SpringHelper class to initialize standalone and web based applications!


              Thanks for the post E. Hardy!

              Comment


              • #8
                Just a note to anybody reading this thread that since Spring 1.1.4 ContextLoader has had support for loading a shared parent context which is done via the ContextSingletonBeanFactoryLocator mechanism. This is essentially the same code I pointed to before in this thread, but now it's built in.

                See the ContextLoader JavaDocs.

                Comment


                • #9
                  That was easy. Thanks.

                  Did I miss this in the documentation or should I request that it be added to the docs?

                  Comment


                  • #10
                    Yeah, this really should be documented in the bean chapter, around the section "Creating an ApplicationContext from a web application". Will add a Jira issue...

                    Comment


                    • #11
                      multiple wars, jars and overriding bean definitions problem

                      We're using Spring 1.1.5, and this seemed like an appropriate place to ask, so here goes:

                      We're using a single ear, but multiple wars, ejb jars, and dao jars.
                      We have adopted a layered approach (dao separate from ejb, etc.), but we're running into that
                      "Overriding bean definition message" repeatedly.

                      In our packaging scheme, we use a beanRefContext.xml file in each jar. In each jar, the beanRefContext.xml file is actually a ClassPathXmlApplicationContext which pulls in the definitions of the beans, the datasource, and the hibernate sessionFactory. The ClassPathXmlApplicationContext is named according to the jar name (to avoid conflicts).

                      Each of the EJBs uses the classpath*:beanRefContext.xml as the BeanFactoryPath. I've toyed with web.xml and its settings for the ContextLoaderListener.

                      The documentation on 1.1.5 points out:
                      "However, if there are multiple sibling web-apps at the top of the hierarchy, it is problematic to create an ApplicationContext for each web-app which consists of mostly identical bean definitions from lower layers, as there may be issues due to increased memory usage, issues with multiple copies of beans... and possible issues due to side-effects."

                      The documentation then suggests using the ContextSingletonBeanFactoryLocator or the SingletonBeanFactoryLocator - but how?

                      I quickly tried extending the AbstractStatelessSessionBean to use the ContextSingletonBeanFactoryLocator, but, at first blush, that didn't seem to work.

                      Any suggestions?

                      Thanks so much (in advance)!

                      Comment


                      • #12
                        You can take a look at the ejbtest integration test app in the autobuilds portion of the Spring CVS tree, for an example of using a shared context from EJBs:

                        http://cvs.sourceforge.net/viewcvs.p.../apps/ejbtest/

                        Regards,

                        Comment

                        Working...
                        X