spring-projects / spring-framework

Spring Framework
https://spring.io/projects/spring-framework
Apache License 2.0
56.39k stars 38.05k forks source link

SingletonBeanFactoryLocator recursion problem [SPR-719] #5447

Closed spring-projects-issues closed 12 years ago

spring-projects-issues commented 19 years ago

Archie Cobbs opened SPR-719 and commented

I am trying to wire up an application using embedded Jetty web server. In order to make the service beans available to the Jetty servlets, I am using ContextLoader.loadParentContext() to set a parent context for the root web context, e.g.:

<context-param>
    <param-name>locatorFactorySelector</param-name>
    <param-value>classpath*:com/awarix/trak/server/**/beanRefFactory.xml</param-value>
</context-param>
<context-param>
    <param-name>parentContextKey</param-name>
    <param-value>com.awarix.trak.server</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

My application is also started using an "AppLauncher" class that I wrote that does something like this:

// Start application
try {
    ContextSingletonBeanFactoryLocator
      .getInstance(getSelector(pname)).useBeanFactory(pname);
} catch (BeansException e) {
    LOG.error("failed to launch application", e);
    e.printStackTrace(System.err);
    System.exit(1);
}

The servlet context is configured to use the same selector and context key as is used by the above code (not shown), to ensure that the ContextLoader finds the same application context for its parent as is used by the rest of the application.

The problem is that recursion occurs in the SingletonBeanFactoryLocator.useBeanFactory() method:

        else {
            // this group definition doesn't exist, we need to try to load it
            if (logger.isDebugEnabled()) {
                logger.debug("Factory group with resource name [" + this.resourceName
                        + "] requested. Creating new instance.");
            }

            BeanFactory groupContext = createDefinition(this.resourceName, factoryKey);

            bfg = new BeanFactoryGroup();
            bfg.definition = groupContext;
            bfg.refCount = 1;
            this.bfgInstancesByKey.put(this.resourceName, bfg);
            this.bfgInstancesByObj.put(groupContext, bfg);
        }

Note that the 'bfg' is not added to bfgInstancesByKey until after createDefinition() is invoked. This causes the second (reentrant) invocation of useBeanFactory() (the one from the web context) to fail to find the beans that are trying to be created in the first invocation (the one from the app launcher).

I.e., here is the sequence of events:

I don't know if this is a Spring bug or if what I'm doing is "out of spec". In any case the behavior is not intuitive.

It may be possible to fix SingletonBeanFactoryLocator to handle the reentrancy (e.g., register the key in bfgInstancesByKey first, then create the definition).

Otherwise, I'd like to know the "right" way to do what I'm trying to do, which is simply get Jetty working as an embedded web server and give it access to my (singleton) application service beans.


Affects: 1.1.4

Attachments:

spring-projects-issues commented 19 years ago

Archie Cobbs commented

Candidate patch to fix this problem.

(Note CR's are stripped, I use Linux not Windows)

spring-projects-issues commented 19 years ago

Archie Cobbs commented

The patch brings up the question: should all BeanFactories in general support the notion of being "initialized" ?

I.e., if we added a method to the BeanFactory interface like "public void initialize()" then the 'instanceof' checks in the patch would not be necessary.

Seems like this would be a desirable change, so that various code like the code discussed here can always ensure an orderly initialization sequence. As it stands, things are a bit non-deterministic, because different BeanFactory implementations can initialize whenver they feel like it, causing other problems similar to the one described in this bug.

E.g., as the patch stands, it won't fix this problem for somebody's custom BeanFactory that isn't an instanceof the checked classes.

spring-projects-issues commented 19 years ago

Colin Sampaleanu commented

patch applied