Uploaded image for project: 'Spring Framework'
  1. Spring Framework
  2. SPR-9335

Unsafe fallback pointcut construction in AspectJExpressionPointcut

    Details

    • Last commented by a User:
      false

      Description

      Hi,

      I have a Java EE application consisting of multiple OSGi bundles running within Apache Felix container on Weblogic 10.3. Spring DM Extender is responsible for loading application contexts of my Spring-powered bundles.

      After switch from Spring 3.0.5.RELEASE to Spring 3.1.1.RELEASE the following error arised in a couple of bundles:

      Caused by: java.lang.IllegalArgumentException: warning no match for this type name: pl.some.package.SomeClass [Xlint:invalidAbsoluteTypeName]
              at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:302)
              at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:207)
              at org.springframework.aop.aspectj.AspectJExpressionPointcut.getFallbackPointcutExpression(AspectJExpressionPointcut.java:358)
              at org.springframework.aop.aspectj.AspectJExpressionPointcut.getShadowMatch(AspectJExpressionPointcut.java:409)
              at org.springframework.aop.aspectj.AspectJExpressionPointcut.matches(AspectJExpressionPointcut.java:272)
              at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:225)
              at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:263)
              at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:295)
              at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:117)
              at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:87)
              at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:68)
              at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:359)
              at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:322)
              at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:407)
              at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(AbstractAutowireCapableBeanFactory.java:1598)
      

      It prevents some bundles from starting successfully. I did some debugging and found out that this exception is thrown while AspectJAwareAdvisorAutoProxyCreator is trying to match

      target(pl.some.package.SomeClass) && @annotation(pl.some.package.SomeAnnotation)
      

      pointcut against a bean which is actually an object retrieved from Weblogic JNDI registry (weblogic.jdbc.common.internal.RmiDataSource data source to be precise). pl.some.package.SomeClass is my bundle's internal class that is visible only to classloader dedicated to this bundle. On the other hand bundle's classloader is unable to load Weblogic's weblogic.jdbc.common.internal.RmiDataSource class.

      After a small investigation i discovered that the following change in org.springframework.aop.aspectj.AspectJExpressionPointcut.getShadowMatch() method is responsible for mentioned error:

      Spring-3.0.5

      if (shadowMatch == null) {
      	try {
      		shadowMatch = this.pointcutExpression.matchesMethodExecution(targetMethod);
      	}
      	catch (ReflectionWorld.ReflectionWorldException ex) {
      		// Failed to introspect target method, probably because it has been loaded
      		// in a special ClassLoader. Let's try the original method instead...
      		if (targetMethod == originalMethod) {
      			shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
      		}
      		else {
      			try {
      				shadowMatch = this.pointcutExpression.matchesMethodExecution(originalMethod);
      			}
      			catch (ReflectionWorld.ReflectionWorldException ex2) {
      				// Could neither introspect the target class nor the proxy class ->
      				// let's simply consider this method as non-matching.
      				shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
      			}
      		}
      	}
      	this.shadowMatchCache.put(targetMethod, shadowMatch);
      }
      

      versus

      Spring-3.1.1

      if (shadowMatch == null) {
      	try {
      		shadowMatch = this.pointcutExpression.matchesMethodExecution(targetMethod);
      	}
      	catch (ReflectionWorld.ReflectionWorldException ex) {
      		// Failed to introspect target method, probably because it has been loaded
      		// in a special ClassLoader. Let's try the original method instead...
      		try {
      			fallbackPointcutExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
      			shadowMatch = fallbackPointcutExpression.matchesMethodExecution(methodToMatch);
      		} catch (ReflectionWorld.ReflectionWorldException e) {
      			if (targetMethod == originalMethod) {
      				shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
      			}
      			else {
      				try {
      					shadowMatch = this.pointcutExpression.matchesMethodExecution(originalMethod);
      				}
      				catch (ReflectionWorld.ReflectionWorldException ex2) {
      					// Could neither introspect the target class nor the proxy class ->
      				        // let's simply consider this method as non-matching.
      					methodToMatch = originalMethod;
      					fallbackPointcutExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
      					try {
      						shadowMatch = fallbackPointcutExpression.matchesMethodExecution(methodToMatch);
      					} catch (ReflectionWorld.ReflectionWorldException e2) {
      						shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
      					}
      				}
      			}
      		}
      	}
      	if (shadowMatch.maybeMatches() && fallbackPointcutExpression!=null) {
      		shadowMatch = new DefensiveShadowMatch(shadowMatch,
      			fallbackPointcutExpression.matchesMethodExecution(methodToMatch));
      	}
      	this.shadowMatchCache.put(targetMethod, shadowMatch);
      }
      

      Normally AspectJExpressionPointcut builds PointcutExpression using AspectJ's PointcutParser and classloader fetched via Thread.currentThread().getContextClassLoader() method. Spring DM Extender ensures that at this point of context creation provided classloader delegates to bundle's internal classloader. Thanks to that creation of AspectJExpressionPointcut.pointcutExpression (equal to parsing pointcut expression by AspectJ classes) works fine. Unfortunately PointcutExpression that uses bundle's classloader cant't match weblogic.jdbc.common.internal.RmiDataSource.getConnection() method of the problematic bean. ReflectionWorldException exception is thrown and Spring tries to construct fallbackPointcutExpression using classloader that loaded weblogic.jdbc.common.internal.RmiDataSource class:

      private PointcutExpression getFallbackPointcutExpression(Class<?> targetClass) {
      	ClassLoader classLoader = targetClass.getClassLoader();
      	return classLoader == null ? this.pointcutExpression : buildPointcutExpression(classLoader);
      }
      

      Parsing pointcut expression using such classloader fails because this classloader doesn't see pl.some.package.SomeClass. AspectJ's PointcutParser throws IllegalArgumentException in this case:

      public PointcutExpression parsePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters) 
           throws UnsupportedPointcutPrimitiveException, IllegalArgumentException {
      	PointcutExpressionImpl pcExpr = null;
      	try {
      		Pointcut pc = resolvePointcutExpression(expression, inScope, formalParameters);
      		pc = concretizePointcutExpression(pc, inScope, formalParameters);
      		validateAgainstSupportedPrimitives(pc, expression); // again, because we have now followed any ref'd pcuts
      		pcExpr = new PointcutExpressionImpl(pc, expression, formalParameters, getWorld());
      	} catch (ParserException pEx) {
      		throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
      	} catch (ReflectionWorld.ReflectionWorldException rwEx) {
      		throw new IllegalArgumentException(rwEx.getMessage());
      	}
      	return pcExpr;
      }
      

      However Spring is not prepared for IllegalArgumentException in this place (only for ReflectionWorldException):

      try {
      	fallbackPointcutExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
      	shadowMatch = fallbackPointcutExpression.matchesMethodExecution(methodToMatch);
      } catch (ReflectionWorld.ReflectionWorldException e) {
      	if (targetMethod == originalMethod) {
      		shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);					}
      

      Finally IllegalArgumentException is propagated through the call stack and in the end initialization of application context fails. From my point of view this is undesirable behaviour so i report this as a bug.

      Note: i can probably workaround this issue by excluding RmiDataSource bean from autoproxing but this is fragile IMHO.

        Activity

        Hide
        krzysztof.kasprzyk Krzysiek Kasprzyk added a comment -

        Are there any plans for fixing this issue in 3.2.x or 4.0.x line?

        Show
        krzysztof.kasprzyk Krzysiek Kasprzyk added a comment - Are there any plans for fixing this issue in 3.2.x or 4.0.x line?
        Hide
        juergen.hoeller Juergen Hoeller added a comment -

        We're defensively handling fallback expression parsing now, never causing an exception from it. This will be available in both 4.0.4 and 3.2.9.

        Juergen

        Show
        juergen.hoeller Juergen Hoeller added a comment - We're defensively handling fallback expression parsing now, never causing an exception from it. This will be available in both 4.0.4 and 3.2.9. Juergen
        Hide
        juergen.hoeller Juergen Hoeller added a comment -

        Krzysiek, does this work for you now, against 4.0.4 and/or the latest 3.2.9 snapshot? We'll be releasing 4.0.5 and 3.2.9 in about a week's time, so it's a great time to give it a try...

        Juergen

        Show
        juergen.hoeller Juergen Hoeller added a comment - Krzysiek, does this work for you now, against 4.0.4 and/or the latest 3.2.9 snapshot? We'll be releasing 4.0.5 and 3.2.9 in about a week's time, so it's a great time to give it a try... Juergen
        Hide
        jpd9808 Jason Day added a comment -

        I was having the same issue when I upgraded from 3.0.4 to 3.2.8 (Apache Felix container + Eclipse blueprint 1.0.2). I pulled the 3.2.9.BUILD-SNAPSHOT and can confirm this change resolved the error for me.

        Show
        jpd9808 Jason Day added a comment - I was having the same issue when I upgraded from 3.0.4 to 3.2.8 (Apache Felix container + Eclipse blueprint 1.0.2). I pulled the 3.2.9.BUILD-SNAPSHOT and can confirm this change resolved the error for me.
        Hide
        juergen.hoeller Juergen Hoeller added a comment -

        Good to hear, Jason! Thanks for your feedback...

        Juergen

        Show
        juergen.hoeller Juergen Hoeller added a comment - Good to hear, Jason! Thanks for your feedback... Juergen

          People

          • Assignee:
            juergen.hoeller Juergen Hoeller
            Reporter:
            krzysztof.kasprzyk Krzysiek Kasprzyk
            Last updater:
            Juergen Hoeller
          • Votes:
            5 Vote for this issue
            Watchers:
            9 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:
              Days since last comment:
              1 year, 1 week, 3 days ago