Skip to content

Modification in AbstractAutowireCapableBeanFactory to prevent stackoverflow errors causes context not to load. [SPR-15125] #19692

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
spring-projects-issues opened this issue Jan 10, 2017 · 9 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: regression A bug that is also a regression
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Jan 10, 2017

Jeff Maxwell opened SPR-15125 and commented

Modification in AbstractAutowireCapableBeanFactory to prevent StackOverflowErrors causes context not to load with org.springframework.beans.factory.NoSuchBeanDefinitionException.

This commit causes our code to throw the exception below.

Earlier versions of AbstractAutowireCapableBeanFactory work fine.

Note that the reference.service.instrumentEhcache bean is created via a subclass of org.springframework.cache.ehcache.EhCacheFactoryBean

The context being loaded is rather complex, I have not yet been able to create a minimal test example.

2017-01-10 11:24:38.991 17201 [main] ERROR o.s.test.context.TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@59d016c9] to prepare test instance [*.*.*.*.*.*.PositionAccountManagerTest@16ca4532]
java.lang.IllegalStateException: Failed to load ApplicationContext
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
	at org.springframework.test.context.junit4.statements.RunPrepareTestInstanceCallbacks.evaluate(RunPrepareTestInstanceCallbacks.java:63)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
	at org.springframework.test.context.junit4.statements.SpringFailOnTimeout.evaluate(SpringFailOnTimeout.java:87)
	at org.springframework.test.context.junit4.statements.ProfileValueChecker.evaluate(ProfileValueChecker.java:101)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.springframework.test.context.junit4.statements.ProfileValueChecker.evaluate(ProfileValueChecker.java:101)
	at org.springframework.test.context.junit4.rules.SpringClassRule$TestContextManagerCacheEvictor.evaluate(SpringClassRule.java:242)
	at org.junit.rules.RunRules.evaluate(RunRules.java:20)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'reference.referenceCache' defined in URL [jar:file:/*/*/*/*/*/*/*/*/*/*.jar!/*/*/*/reference/cache/ReferenceCache.class]: Unsatisfied dependency expressed through constructor parameter 14: Error creating bean with name 'reference.service.instrumentCache' defined in URL [jar:file:/*/*/*/*/*/*/*/*/*/*.jar!/*/*/*/reference/cache/InstrumentCache.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type [net.sf.ehcache.Ehcache] found for dependency [net.sf.ehcache.Ehcache]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=reference.service.instrumentEhcache)}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [net.sf.ehcache.Ehcache] found for dependency [net.sf.ehcache.Ehcache]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=reference.service.instrumentEhcache)}; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'reference.service.instrumentCache' defined in URL [jar:file:/*/*/*/*/*/*/*/*/*/*.jar!/com/theice/cds/reference/cache/InstrumentCache.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type [net.sf.ehcache.Ehcache] found for dependency [net.sf.ehcache.Ehcache]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=reference.service.instrumentEhcache)}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [net.sf.ehcache.Ehcache] found for dependency [net.sf.ehcache.Ehcache]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=reference.service.instrumentEhcache)}
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:189)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1147)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1050)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:751)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541)
	at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:128)
	at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
	at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:108)
	at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:251)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
	... 29 common frames omitted
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'reference.service.instrumentCache' defined in URL [jar:file:/*/*/*/*/*/*/*/*/*/*.jar!/*/*/*/reference/cache/InstrumentCache.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type [net.sf.ehcache.Ehcache] found for dependency [net.sf.ehcache.Ehcache]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=reference.service.instrumentEhcache)}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [net.sf.ehcache.Ehcache] found for dependency [net.sf.ehcache.Ehcache]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=reference.service.instrumentEhcache)}
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:189)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1147)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1050)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:207)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1239)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1079)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1044)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:835)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
	... 47 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [net.sf.ehcache.Ehcache] found for dependency [net.sf.ehcache.Ehcache]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=reference.service.instrumentEhcache)}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1431)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1082)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1044)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:835)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
	... 62 common frames omitted

Affects: 4.3.3

Issue Links:

Referenced from: commits 32fc855, 24ebd15

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jan 11, 2017

Juergen Hoeller commented

#19119 was a rather important fix since prior to this change we completely bypassed getSingletonFactoryBeanForTypeCheck's currently-in-creation checks, leading to full initialization of the FactoryBean instance for type checking even in circular reference scenarios... with a stack overflow potential even if the type wouldn't match anyway.

So in your case, we seem to back out of checking your FactoryBean's type because we detect a currently-in-creation scenario. You could try to debug this with a breakpoint in getSingletonFactoryBeanForTypeCheck, stepping through the currently-in-creation conditions there.

Are you possibly declaring this FactoryBean on a configuration class, trying to wire its result into the configuration class itself? In such a scenario, please note that the only clean way out is declaring such an @Bean method as static, allowing its instance to be created without relying on a fully initialized configuration class instance around.

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Or are you possibly trying to inject a FactoryBean's object outcome into that very same FactoryBean itself?

In any case, please show some configuration snippets for a clearer indication. There is something special about your setup that we do not have a unit test for.

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

I've added a lenient fallback to an early singleton reference of the currently created FactoryBean. Even if this is not recommended, with 4.3's self injection capabilities, it should arguably work.

This will make into tomorrow's 4.3.6.BUILD-SNAPSHOT. If your scenario still isn't working then, please reopen this issue.

@spring-projects-issues
Copy link
Collaborator Author

Jeff Maxwell commented

Are you possibly declaring this FactoryBean on a configuration class, trying to wire its result into the configuration class itself?

Not that I am aware.

Or are you possibly trying to inject a FactoryBean's object outcome into that very same FactoryBean itself?

It does not appear to do this.

In any case, please show some configuration snippets for a clearer indication. There is something special about your setup that we do not have a unit test for.

I have struggled to create a minimal unit test of this issue as the configuration/code base is rather labyrinthine.

I've added a lenient fallback to an early singleton reference of the currently created FactoryBean. Even if this is not recommended, with 4.3's self injection capabilities, it should arguably work.

I will pull and retest.

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Unfortunately, there was still some stack overflow potential left, so I haven't committed any change yet. Self references to a FactoryBean - which is what I tested against there out of my wild guess - are inherently dangerous, it seems :-( I'll mark this issues as reopened.

The declarations of your EhCacheFactoryBean subclass and the point where you inject it would help me a lot. Otherwise the only way forward is debugging on your end.

@spring-projects-issues
Copy link
Collaborator Author

Jeff Maxwell commented

We believe that we have narrowed the issue a bit.
There are a couple of cache loaders, including our loader for reference.service.instrumentEhcache, that depend on other caches.
If we remove these dependencies from the reference.service.instrumentEhcache cache loader we do not see the issue.

Does this help?

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

I've added additional detection of statically declared FactoryBean types in bean class hierarchies as well as static factory methods. So instead of trying to create the full bean instance (which leads to a potential stack overflow), we're now falling back to static introspection if we can't create an early FactoryBean instance. This works fine for our unit tests and should work fine for a standard FactorBean variant like EhCacheFactoryBean as well, as long as the implementation hierarchy declares the FactoryBean's object type in some implements or extends clause.

I'll ping you once this is available in a 4.3.6.BUILD-SNAPSHOT. Would be great if you could give it an early try then...

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

This is available in the latest 4.3.6.BUILD-SNAPSHOT now. Please give it a try if you have the chance...

(See http://projects.spring.io/spring-framework/ for how to obtain a snapshot from our snapshot repository.)

@spring-projects-issues
Copy link
Collaborator Author

Jeff Maxwell commented

It works.
Thanks for all your effort.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: regression A bug that is also a regression
Projects
None yet
Development

No branches or pull requests

2 participants