Issue Details (XML | Word | Printable)

Key: SPR-3223
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Minor Minor
Assignee: Juergen Hoeller
Reporter: chris tam
Votes: 0
Watchers: 2
Operations

If you were logged in you would be able to see more operations.
Spring Framework

AspectJExpressionPointcut throws ReflectionWorldException when both <aop:config> and <lang:groovy> exists in same xml file

Created: 01/Mar/07 09:29 AM   Updated: 03/Mar/07 12:40 PM   Resolved: 02/Mar/07 09:17 AM
Component/s: SpringAOP
Affects Version/s: 2.0.3
Fix Version/s: 2.0.3

Time Tracking:
Not Specified

Environment: Win 2000

Virtual Machine: Sun JVM - 1.4.2
Platform: Standalone


 Description  « Hide

I am very sorry to bother Spring team again. The AspectJ Weaver will throw ReflectionWorldException when an application context xml file contains both <aop:config> and <lang:groovy> elements. The problem forces all sping users to use either <aop:config> or <lang:groovy>, but never both. For example:
...
<aop:config>
<aop:pointcut id="PersonServiceOperation" expression="execution(* org.test.PersonService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="PersonServiceOperation"/>
</aop:config>
...
<lang:groovy id="GroovyPersonService" script-source="classpath:org/test/GroovyPersonService.groovy"/>
...
I have read all the relevent codes and find out that the exception is thrown by AspectJExpressionPointcut class in the following code:
...
private ShadowMatch getShadowMatch(Method method) {
synchronized (this.shadowMapCache) {
ShadowMatch shadowMatch = (ShadowMatch) this.shadowMapCache.get(method);
if (shadowMatch == null) { shadowMatch = this.pointcutExpression.matchesMethodExecution(method); // throw ReflectionWorldException this.shadowMapCache.put(method, shadowMatch); }
return shadowMatch;
}
}
...
The ReflectionWorldException is thrown by the pointcutExpression. According to the 2.0.3 source code, <aop:config> element will auto trigger AbstractAdvisorAutoProxyCreator which will run to the above getShadowMatch method. If the parameter passed to the getShadowMethod belong to a Groovy script object, the pointcutExpression object cannot see the Groovy script object method because the pointcutExpression object and the Groovy script object belongs to different ClassLoaders. Please provide a workaround solution to this problem. For example, in the AspectJExpressionPointcut file

...
public boolean matches(Method method, Class targetClass, boolean beanHasIntroductions) {
checkReadyToMatch();

// TODO: need to check whether a temporary pointcutExpression is needed or not
// build a temporary pointcutExpression to be used by getShadowMatch() method
PointcutExpression old_pointcutExpression = this.pointcutExpression ; // save original pointcutExpression

// create a temporary PointcutParser using the targetClass classloader so that the pointcutExpression can see the Groovy
// script object methods
PointcutParser temp_pointcutParser =
PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
DEFAULT_SUPPORTED_PRIMITIVES, targetClass.getClassLoader());

// create a temporary pointcutExpression
PointcutParameter[] pointcutParameters = new PointcutParameter[0]; // MUST be modified, just example here
this.pointcutExpression = temp_pointcutParser.parsePointcutExpression(
replaceBooleanOperators(getExpression()), this.pointcutDeclarationScope, pointcutParameters);

Method methodToMatch = findMethodToMatchAgainst(method,targetClass);
ShadowMatch shadowMatch = getShadowMatch(methodToMatch);

// restore the original pointcutExpression
this.pointcutExpression = old_pointcutExpression ;
temp_pointcutParser = null ;
...

The above example is very messy and silly. I know that Spring team will provide a better solution. The above code is only a temporary solution I currently use. Thanks a lot for any help from Spring team.

cheers
chris tam
xenium



Juergen Hoeller added a comment - 02/Mar/07 09:17 AM

Thanks for pointing this out! I've fixed this through a simple fallback to the proxy method when the ShadowMatch for the target method failed. After all, AOP advice for scripted objects is not supposed to match against the target class in the first place...

This should be available in the next 2.0.3 snapshot.

Juergen


chris tam added a comment - 02/Mar/07 09:38 AM

Thanks a lot for your help. I will download and use the next snapshot in my current development..

Thanks
chris tam
xenium


Mark Menard added a comment - 03/Mar/07 09:42 AM

Juergen,

You say, "After all, AOP advice for scripted objects is not supposed to match against the target class in the first place... " I'm new to Spring and AOP. Does this mean that you can't use AOP to wrap a scripted bean in a transaction?

In my particular case all of my scripted service beans implement an interface, written in Java and compiled ahead of time. I use the interfaces in my aop:config entry in my xml file. Will my Groovy beans be wrapped in a transaction?

Thanks,

Mark


chris tam added a comment - 03/Mar/07 11:48 AM

Dear Mark

I think you have misunderstood Juergen comment. Juergen is talking about the source code in the AspectJExpressionPointcut java class."target class" refers to a variable in the source code. If you need to fully understand Juergen comment, you need to read the AspectJExpressionPointcut java class source code.

You can use AOP to wrap any scripted bean in a transaction with Juergen fix. I have already tested to wrap a groovy script bean in a transaction with Juergen fix and it passes all the junit tests. The groovy script bean can insert, delete, update, commit and rollback successfully. The fix should be in the next snapshot 20070302. If you want to test before the snapshot is available. You can get the latest AspectJExpressionPointcut.java from the CVS server and compile the spring.

Thanks
chris tam
xenium


Juergen Hoeller added a comment - 03/Mar/07 12:40 PM

Indeed: Scripted objects can of course be adviced based on their interfaces, with pointcut expressions matching methods defined in those interfaces. The only difference to regular Java classes is that you can't match against the actual target class behind those interfaces (which doesn't even have to exist: scripted objects may implement interface methods as direct method scripts, with no notion of an actual target class).

Juergen