Results 1 to 7 of 7

Thread: ConfigurationClassPostProcessor: IllegalStateException when starting test case

  1. #1
    Join Date
    Jul 2011
    Posts
    5

    Default ConfigurationClassPostProcessor: IllegalStateException when starting test case

    I am currently migrating a fairly large application from Spring 3.0.0 to Spring 3.0.5. It's generally going well, but I am having difficulty with a few test cases that are using the annotation approach to bootstrapping the container:

    Code:
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "classpath:myapp/application-context.xml" })
    @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
    @Transactional
    public abstract class AbstractPublishingIntegrationTest
    {
    ...
    }
    The issue is that a bean post processor (an instance of ConfigurationClassPostProcessor) is having its postProcessBeanDefinitionRegistry operation invoked twice. On the second attempt it's blowing up with an IllegalStateException. The trouble is I have no idea what is causing it to be run twice.

    The first time it is invoked, this is the stack trace:
    Code:
    Thread [main] (Suspended (breakpoint at line 133 in ConfigurationClassPostProcessor))	
    	ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(BeanDefinitionRegistry) line: 133	
    	GenericApplicationContext(AbstractApplicationContext).invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory) line: 599	
    
    
    	GenericApplicationContext(AbstractApplicationContext).refresh() line: 407	
    
    	GenericXmlContextLoader(AbstractGenericContextLoader).loadContext(String...) line: 84	
    	GenericXmlContextLoader(AbstractGenericContextLoader).loadContext(String...) line: 1	
    	TestContext.loadApplicationContext() line: 280	
    	TestContext.getApplicationContext() line: 304	
    	DependencyInjectionTestExecutionListener.injectDependencies(TestContext) line: 109	
    	DependencyInjectionTestExecutionListener.prepareTestInstance(TestContext) line: 75	
    	TestContextManager.prepareTestInstance(Object) line: 321	
    	SpringJUnit4ClassRunner.createTest() line: 220	
    	SpringJUnit4ClassRunner$1.runReflectiveCall() line: 301	
    	SpringJUnit4ClassRunner$1(ReflectiveCallable).run() line: 15	
    	SpringJUnit4ClassRunner.methodBlock(FrameworkMethod) line: 303	
    	SpringJUnit4ClassRunner.runChild(FrameworkMethod, RunNotifier) line: 240	
    	SpringJUnit4ClassRunner(BlockJUnit4ClassRunner).runChild(Object, RunNotifier) line: 44	
    	SpringJUnit4ClassRunner(ParentRunner<T>).runChildren(RunNotifier) line: 180	
    	ParentRunner<T>.access$000(ParentRunner, RunNotifier) line: 41	
    	ParentRunner$1.evaluate() line: 173	
    	RunBefores.evaluate() line: 28	
    	RunBeforeTestClassCallbacks.evaluate() line: 61	
    	RunAfters.evaluate() line: 31	
    	RunAfterTestClassCallbacks.evaluate() line: 70	
    	SpringJUnit4ClassRunner(ParentRunner<T>).run(RunNotifier) line: 220	
    	SpringJUnit4ClassRunner.run(RunNotifier) line: 180	
    	JUnit4TestClassReference(JUnit4TestReference).run(TestExecution) line: 46	
    	TestExecution.run(ITestReference[]) line: 38	
    	RemoteTestRunner.runTests(String[], String, TestExecution) line: 467	
    	RemoteTestRunner.runTests(TestExecution) line: 683	
    	RemoteTestRunner.run() line: 390	
    	RemoteTestRunner.main(String[]) line: 197
    I've highlighted the line that appears to be particularly important. The second time the operation is invoked, this is the stack trace:

    Code:
    Thread [main] (Suspended (breakpoint at line 133 in ConfigurationClassPostProcessor))	
    	ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(BeanDefinitionRegistry) line: 133	
    	ChildApplicationContextFactory$ChildApplicationContext(AbstractApplicationContext).invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory) line: 586	
    	ChildApplicationContextFactory$ChildApplicationContext(AbstractApplicationContext).refresh() line: 407	
    	ChildApplicationContextFactory$ApplicationContextState.start() line: 684	
    	ChildApplicationContextFactory(AbstractPropertyBackedBean).start(boolean) line: 665	
    	ChildApplicationContextFactory(AbstractPropertyBackedBean).getState(boolean) line: 234	
    	ChildApplicationContextFactory.getApplicationContext() line: 368	
    	SubsystemProxyFactory$1.invoke(MethodInvocation) line: 60	
    	ReflectiveMethodInvocation.proceed() line: 172	
    	JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 202	
    	$Proxy9.subsituteHost(String) line: not available	
    	NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]	
    	NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39	
    	DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25	
    	Method.invoke(Object, Object...) line: 597	
    	CglibSubclassingInstantiationStrategy(SimpleInstantiationStrategy).instantiate(RootBeanDefinition, String, BeanFactory, Object, Method, Object[]) line: 145	
    	ConstructorResolver.instantiateUsingFactoryMethod(String, RootBeanDefinition, Object[]) line: 570	
    	DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).instantiateUsingFactoryMethod(String, RootBeanDefinition, Object[]) line: 983	
    	DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBeanInstance(String, RootBeanDefinition, Object[]) line: 879	
    	DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 485	
    	DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 456	
    	AbstractBeanFactory$1.getObject() line: 291	
    	DefaultListableBeanFactory(DefaultSingletonBeanRegistry).getSingleton(String, ObjectFactory) line: 222	
    	DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 288	
    	DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 190	
    	DefaultListableBeanFactory.preInstantiateSingletons() line: 580	
    	GenericApplicationContext(AbstractApplicationContext).finishBeanFactoryInitialization(ConfigurableListableBeanFactory) line: 895	
    
    	GenericApplicationContext(AbstractApplicationContext).refresh() line: 425	
    
    	GenericXmlContextLoader(AbstractGenericContextLoader).loadContext(String...) line: 84	
    	GenericXmlContextLoader(AbstractGenericContextLoader).loadContext(String...) line: 1	
    	TestContext.loadApplicationContext() line: 280	
    	TestContext.getApplicationContext() line: 304	
    	DependencyInjectionTestExecutionListener.injectDependencies(TestContext) line: 109	
    	DependencyInjectionTestExecutionListener.prepareTestInstance(TestContext) line: 75	
    	TestContextManager.prepareTestInstance(Object) line: 321	
    	SpringJUnit4ClassRunner.createTest() line: 220	
    	SpringJUnit4ClassRunner$1.runReflectiveCall() line: 301	
    	SpringJUnit4ClassRunner$1(ReflectiveCallable).run() line: 15	
    	SpringJUnit4ClassRunner.methodBlock(FrameworkMethod) line: 303	
    	SpringJUnit4ClassRunner.runChild(FrameworkMethod, RunNotifier) line: 240	
    	SpringJUnit4ClassRunner(BlockJUnit4ClassRunner).runChild(Object, RunNotifier) line: 44	
    	SpringJUnit4ClassRunner(ParentRunner<T>).runChildren(RunNotifier) line: 180	
    	ParentRunner<T>.access$000(ParentRunner, RunNotifier) line: 41	
    	ParentRunner$1.evaluate() line: 173	
    	RunBefores.evaluate() line: 28	
    	RunBeforeTestClassCallbacks.evaluate() line: 61	
    	RunAfters.evaluate() line: 31	
    	RunAfterTestClassCallbacks.evaluate() line: 70	
    	SpringJUnit4ClassRunner(ParentRunner<T>).run(RunNotifier) line: 220	
    	SpringJUnit4ClassRunner.run(RunNotifier) line: 180	
    	JUnit4TestClassReference(JUnit4TestReference).run(TestExecution) line: 46	
    	TestExecution.run(ITestReference[]) line: 38	
    	RemoteTestRunner.runTests(String[], String, TestExecution) line: 467	
    	RemoteTestRunner.runTests(TestExecution) line: 683	
    	RemoteTestRunner.run() line: 390	
    	RemoteTestRunner.main(String[]) line: 197
    Again, the highlighted line seems particularly relevant. Basically, we seem to be running through the method AbstractApplicationContext.refresh(), and the bean post processor is indirectly being invoked twice within that method. The source of that method is this:

    Code:
    try {
    	// Allows post-processing of the bean factory in context subclasses.
    	postProcessBeanFactory(beanFactory);
    
    	// Invoke factory processors registered as beans in the context.
    	invokeBeanFactoryPostProcessors(beanFactory);
    
    	// Register bean processors that intercept bean creation.
    	registerBeanPostProcessors(beanFactory);
    
    	// Initialize message source for this context.
    	initMessageSource();
    
    	// Initialize event multicaster for this context.
    	initApplicationEventMulticaster();
    
    	// Initialize other special beans in specific context subclasses.
    	onRefresh();
    
    	// Check for listener beans and register them.
    	registerListeners();
    
    	// Instantiate all remaining (non-lazy-init) singletons.
    	finishBeanFactoryInitialization(beanFactory);
    
    	// Last step: publish corresponding event.
    	finishRefresh();
    }
    Again, relevant lines highlighted.

    So the question is: does anyone know what might possibly be causing the post processor to be invoked twice from this code? I'm sure it goes without saying, but the tests were working correctly when using Spring 3.0.0.

    All ideas and suggestions very gratefully received.


    Brian

  2. #2
    Join Date
    Jul 2011
    Posts
    5

    Default

    Sorry - scrub that. Looking through the second stack trace I can see that there is something odd happening in our code. Still not quite sure why it worked previously and doesn't any more, but I'll hunt further.

  3. #3
    Join Date
    Jul 2011
    Posts
    5

    Default

    OK. A slightly different take on the same question. I'm now looking at the class ConfigurationClassPostProcessor. This has this method defined in it:

    Code:
    	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    		if (this.postProcessBeanDefinitionRegistryCalled) {
    			throw new IllegalStateException(
    					"postProcessBeanDefinitionRegistry already called for this post-processor");
    		}
    		if (this.postProcessBeanFactoryCalled) {
    			throw new IllegalStateException(
    					"postProcessBeanFactory already called for this post-processor");
    		}
    		this.postProcessBeanDefinitionRegistryCalled = true;
    		processConfigBeanDefinitions(registry);
    	}
    In my application this method is being called more than once, with each call having a different "registry" parameter.

    Is the logic of this method correct? Shouldn't it be only throwing an exception if its invoked more than once on any single registry?


    Regards
    Brian

  4. #4
    Join Date
    Jul 2010
    Posts
    139

    Default

    In case you haven't yet, try adding a breakpoint early on in the applicationContext lifecycle and step through one of your test cases inn debug mode. If you have enough patience, this usually will help you find the trouble spot.

  5. #5
    Join Date
    Jul 2011
    Posts
    5

    Default

    Thanks. I have done this. The problem seems to arise from the fact that we instantiate our own child application contexts which Spring then passes to the registered post processors - as you would expect. Unfortunately, the ConfigurationClassPostProcessor class doesn't seem to care whether it's being invoked twice for different registries or twice for the same registry - it *always* throws an exception on the second invocation. I think this is a fault in the logic of that class. It doesn't seem to take into account applications that contain multiple contexts. In 3.0.0 it didn't test for a second invocation.

    I don't quite understand why this is only occurring when using the @ContextConfiguration annotation, but it looks as if it's causing an additional context layer to be inserted. I still think the logic of the ConfigurationClassPostProcessor is wrong. I think it should only protest if called twice for the same registry.

  6. #6
    Join Date
    Jul 2010
    Posts
    139

    Default

    Recommend submitting a JIRA if you don't get any further assistance on the forum. Also, if you can submit a small project that illustrates your issue, that will help with pinpointing the problem.

  7. #7
    Join Date
    Jul 2011
    Posts
    5

    Default

    Just an update on this in case anyone else stumbles across the same problem. I've submitted an issue in JIRA (https://jira.springsource.org/browse/SPR-8527), and it's been accepted and fixed. The target fix version is 3.1.0 RC1.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •