Spring Framework
  1. Spring Framework
  2. SPR-8070

Race condition in AspectJ pointuct resolution when 'prototype' beans of same type are initialized concurrently

    Details

    • Type: Bug Bug
    • Status: Open
    • Priority: Minor Minor
    • Resolution: Unresolved
    • Affects Version/s: 3.0.3
    • Fix Version/s: None
    • Component/s: Core:AOP
    • Labels:
      None
    • Last commented by a User:
      true

      Description

      We intermittently receive this error in bean initialization from our application:

      org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'foo' defined in file [...]: Initialization of bean failed; nested exception is java.lang.NullPointerException
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:527) ~[org.springframework.beans.jar:3.0.3.RELEASE]
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456) ~[org.springframework.beans.jar:3.0.3.RELEASE]
      	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:310) ~[org.springframework.beans.jar:3.0.3.RELEASE]
      	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190) ~[org.springframework.beans.jar:3.0.3.RELEASE]
      	at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:838) ~[org.springframework.beans.jar:3.0.3.RELEASE]
      	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:780) ~[org.springframework.beans.jar:3.0.3.RELEASE]
      	at org.springframework.beans.factory.support.DefaultListableBeanFactory$DependencyObjectFactory.getObject(DefaultListableBeanFactory.java:1008) ~[org.springframework.beans.jar:3.0.3.RELEASE]
      	at org.springframework.beans.factory.support.DefaultListableBeanFactory$DependencyProvider.get(DefaultListableBeanFactory.java:1023) ~[org.springframework.beans.jar:3.0.3.RELEASE]
      	at ...
      Caused by: java.lang.NullPointerException: null
      	at org.aspectj.weaver.World.resolve(World.java:265) ~[aspectjweaver.jar:1.6.8]
      	at org.aspectj.weaver.World.resolve(World.java:209) ~[aspectjweaver.jar:1.6.8]
      	at org.aspectj.weaver.World.resolve(World.java:244) ~[aspectjweaver.jar:1.6.8]
      	at org.aspectj.weaver.TypeFactory.createParameterizedType(TypeFactory.java:45) ~[aspectjweaver.jar:1.6.8]
      	at org.aspectj.weaver.reflect.JavaLangTypeToResolvedTypeConverter.fromType(JavaLangTypeToResolvedTypeConverter.java:75) ~[aspectjweaver.jar:1.6.8]
      	at org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate.getSuperclass(Java15ReflectionBasedReferenceTypeDelegate.java:139) ~[aspectjweaver.jar:1.6.8]
      	at org.aspectj.weaver.ReferenceType.getSuperclass(ReferenceType.java:905) ~[aspectjweaver.jar:1.6.8]
      	at org.aspectj.weaver.patterns.KindedPointcut.fastMatch(KindedPointcut.java:144) ~[aspectjweaver.jar:1.6.8]
      	at org.aspectj.weaver.internal.tools.PointcutExpressionImpl.couldMatchJoinPointsInType(PointcutExpressionImpl.java:82) ~[aspectjweaver.jar:1.6.8]
      	at org.springframework.aop.aspectj.AspectJExpressionPointcut.matches(AspectJExpressionPointcut.java:233) ~[org.springframework.aop.jar:3.0.3.RELEASE]
      	at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:200) ~[org.springframework.aop.jar:3.0.3.RELEASE]
      	at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:254) ~[org.springframework.aop.jar:3.0.3.RELEASE]
      	at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:286) ~[org.springframework.aop.jar:3.0.3.RELEASE]
      	at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:117) ~[org.springframework.aop.jar:3.0.3.RELEASE]
      	at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:87) ~[org.springframework.aop.jar:3.0.3.RELEASE]
      	at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:68) ~[org.springframework.aop.jar:3.0.3.RELEASE]
      	at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:359) ~[org.springframework.aop.jar:3.0.3.RELEASE]
      	at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:322) ~[org.springframework.aop.jar:3.0.3.RELEASE]
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:407) ~[org.springframework.beans.jar:3.0.3.RELEASE]
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1418) ~[org.springframework.beans.jar:3.0.3.RELEASE]
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519) ~[org.springframework.beans.jar:3.0.3.RELEASE]
      	... 35 common frames omitted
      
      

      We tracked the issue down to the following scenario:

      • A 'prototype' bean definition is injected (for the first time) concurrently by two separate threads (when AOP is in use - e.g. for @Transactional proxies).
      • When Spring post-processes the prototype bean, it tests the defined pointcuts (AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply).
      • Eventually, that uses the thread-shared "org.aspectj.weaver.World" object to do type resolution and other such stuff.
      • Eventually, a JavaLangTypeToResolvedTypeConverter is obtained from World and used concurrently from these two separate threads initializing the prototype bean.
      • The JavaLangTypeToResolvedTypeConverter class is not threadsafe - this Map field is used to track per-call state (i.e. it should really be a stack variable). My guess is the NPE is caused by the unsynchronized/unsafe use of this Map.
        JavaLangTypeToResolvedTypeConverter.java
        	// Used to prevent recursion - we record what we are working on and return it if asked again *whilst* working on it
        	private Map<Type,TypeVariableReferenceType> typeVariablesInProgress 
        				= new HashMap<Type,TypeVariableReferenceType>();
        ...
        	public ResolvedType fromType(Type aType) {
        ...
        		} else if (aType instanceof java.lang.reflect.TypeVariable) {
        			if (typeVariablesInProgress.get(aType)!=null) // check if we are already working on this type
        				return typeVariablesInProgress.get(aType);
        
        ...			
        			typeVariablesInProgress.put(aType,tvrt); // record what we are working on, for recursion case
        			
        ...			
        			typeVariablesInProgress.remove(aType); // we have finished working on it
        			
        			return tvrt;
        		} 
        
      • That "World" class seems to provide a cache/context for type resolution - hence we only see the NullPointerException the first initialization of the prototype bean, thereafter the type is resolved via the cache in "World".

      This race condition is very sensitive to timing, we cannot reliably reproduce it. We are experimenting with a workaround of creating a "throaway" prototype bean from the application main thread just to get the AspectJ "World" primed with the type-resolution cache in a threadsafe manner. Then subsequent getBeans() on the prototype bean concurrently from different application threads should work fine.

        Activity

        Hide
        Eric Sirianni added a comment -

        Looks to be highly related to this thread:

        I am within a spring context where prototype beans are loaded via an ObjectFactory bean. The exception mainly occurs on the first request after starting the server and the system is under load.

        AspectJ bug 337855 corresponds to the above thread.

        So perhaps there is nothing to change in Spring's usage of AspectJ weaver and the issue is entirely an AspectJ one.

        Show
        Eric Sirianni added a comment - Looks to be highly related to this thread : I am within a spring context where prototype beans are loaded via an ObjectFactory bean. The exception mainly occurs on the first request after starting the server and the system is under load. AspectJ bug 337855 corresponds to the above thread. So perhaps there is nothing to change in Spring's usage of AspectJ weaver and the issue is entirely an AspectJ one.
        Hide
        Eric Sirianni added a comment -

        Created AspectJ bug 340806 for this issue.

        Show
        Eric Sirianni added a comment - Created AspectJ bug 340806 for this issue.

          People

          • Assignee:
            Juergen Hoeller
            Reporter:
            Eric Sirianni
            Last updater:
            Trevor Marshall
          • Votes:
            2 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

            • Created:
              Updated:
              Days since last comment:
              3 years, 5 weeks ago