Grails

AOP 2 not working with Grails 1.2 (works with Grails 1.1.x)

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Critical Critical
  • Resolution: Fixed
  • Affects Version/s: 1.2.1
  • Fix Version/s: 1.2.2
  • Component/s: None
  • Labels:
    None

Description

The Spring AOP 2 stuff, using <aop:config>, doesn't seem to work with Grails 1.2. See the attached project for an example. It adds a couple of aspects (found under grails-app/utils) to some services. The aspects simply log some messages at the DEBUG level. Everything is already configured, so just run the application, navigate to the only controller there and check the console output. You should see some messages from the aspects.

Note that this works with Grails 1.1.x.

Issue Links

Activity

Hide
Trevor Hawrysh added a comment -

I have been doing some testing and debugging on this issue to try an isolate the issue.

I wrote some unit tests that create an ApplicationContext from a BeanBuilder and the AOP works correctly. I did an assert that object returned from the getBean method is an instanceof SpringProxy.

While testing with a grails application the service is NOT a proxy.

Looks to me like the beanbuilder for an application is not detecting the AOP advice and creating a proxy.

I also noticed that there was a new class introduced in grails 1.2

/**
 * Tells Spring always to proxy Groovy classes
 * 
 * @author Graeme Rocher
 * @since 1.2
 */
public class GroovyAwareInfrastructureAdvisorAutoProxyCreator

This class is used in the CoreGrailsPlugin.groovy

// replace AutoProxy advisor with Groovy aware one
"org.springframework.aop.config.internalAutoProxyCreator"(GroovyAwareInfrastructureAdvisorAutoProxyCreator)

Show
Trevor Hawrysh added a comment - I have been doing some testing and debugging on this issue to try an isolate the issue. I wrote some unit tests that create an ApplicationContext from a BeanBuilder and the AOP works correctly. I did an assert that object returned from the getBean method is an instanceof SpringProxy. While testing with a grails application the service is NOT a proxy. Looks to me like the beanbuilder for an application is not detecting the AOP advice and creating a proxy. I also noticed that there was a new class introduced in grails 1.2
/**
 * Tells Spring always to proxy Groovy classes
 * 
 * @author Graeme Rocher
 * @since 1.2
 */
public class GroovyAwareInfrastructureAdvisorAutoProxyCreator

This class is used in the CoreGrailsPlugin.groovy
// replace AutoProxy advisor with Groovy aware one
"org.springframework.aop.config.internalAutoProxyCreator"(GroovyAwareInfrastructureAdvisorAutoProxyCreator)

Hide
Trevor Hawrysh added a comment -

I found this test in the testsuite BeanBuilderTests which is passing

 
    void testSpringAOPSupport() {


        def bb = new BeanBuilder()

        bb.beans {
            xmlns aop:"http://www.springframework.org/schema/aop"

            fred(AdvisedPerson) {
                name = "Fred"
                age = 45
            }
            birthdayCardSenderAspect(BirthdayCardSender)

            aop.config("proxy-target-class":true) {
                aspect( id:"sendBirthdayCard",ref:"birthdayCardSenderAspect" ) {
                    after method:"onBirthday", pointcut: "execution(void grails.spring.AdvisedPerson.birthday()) and this(person)"
                }                
            }
        }


        def appCtx = bb.createApplicationContext()
        def fred = appCtx.getBean("fred")
        assertTrue (fred instanceof SpringProxy )


        fred.birthday()

        BirthdayCardSender birthDaySender = appCtx.getBean("birthdayCardSenderAspect")

        assertEquals 1, birthDaySender.peopleSentCards.size()
        assertEquals "Fred", birthDaySender.peopleSentCards[0].name

    }
Show
Trevor Hawrysh added a comment - I found this test in the testsuite BeanBuilderTests which is passing
 
    void testSpringAOPSupport() {


        def bb = new BeanBuilder()

        bb.beans {
            xmlns aop:"http://www.springframework.org/schema/aop"

            fred(AdvisedPerson) {
                name = "Fred"
                age = 45
            }
            birthdayCardSenderAspect(BirthdayCardSender)

            aop.config("proxy-target-class":true) {
                aspect( id:"sendBirthdayCard",ref:"birthdayCardSenderAspect" ) {
                    after method:"onBirthday", pointcut: "execution(void grails.spring.AdvisedPerson.birthday()) and this(person)"
                }                
            }
        }


        def appCtx = bb.createApplicationContext()
        def fred = appCtx.getBean("fred")
        assertTrue (fred instanceof SpringProxy )


        fred.birthday()

        BirthdayCardSender birthDaySender = appCtx.getBean("birthdayCardSenderAspect")

        assertEquals 1, birthDaySender.peopleSentCards.size()
        assertEquals "Fred", birthDaySender.peopleSentCards[0].name

    }
Hide
Burt Beckwith added a comment -

Looks like GroovyAwareInfrastructureAdvisorAutoProxyCreator is the problem. It extends InfrastructureAdvisorAutoProxyCreator which has this implementation of isEligibleAdvisorBean():

protected boolean isEligibleAdvisorBean(String beanName) {
   return (this.beanFactory.containsBeanDefinition(beanName) &&
         this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
}

The role of the AspectJPointcutAdvisor is ROLE_APPLICATION and not ROLE_INFRASTRUCTURE so the list of candidate advisors is empty and no proxy is created.

If you create the equivalent AOP bean configuration in resources.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

   <bean id="theAspect" class="TheAspect"/>
   <bean id="anotherAspect" class="apackage.AnotherAspect"/>

   <aop:config proxy-target-class="true">

      <aop:aspect ref="theAspect" id='theAspectDef'>
         <aop:around pointcut="execution(* TheServiceInterface.service(..))" method="invoke"/>
      </aop:aspect>

      <aop:aspect ref="anotherAspect" id='anotherAspectDef'>
         <aop:around pointcut="execution(* *.AnotherServiceInterface.service(..))" method="invoke"/>
      </aop:aspect>

   </aop:config>

</beans>

then it works fine because the auto proxy creator is an AspectJAwareAdvisorAutoProxyCreator with this implementation of isEligibleAdvisorBean() (from AbstractAdvisorAutoProxyCreator):

protected boolean isEligibleAdvisorBean(String beanName) {
   return true;
}
Show
Burt Beckwith added a comment - Looks like GroovyAwareInfrastructureAdvisorAutoProxyCreator is the problem. It extends InfrastructureAdvisorAutoProxyCreator which has this implementation of isEligibleAdvisorBean():
protected boolean isEligibleAdvisorBean(String beanName) {
   return (this.beanFactory.containsBeanDefinition(beanName) &&
         this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
}
The role of the AspectJPointcutAdvisor is ROLE_APPLICATION and not ROLE_INFRASTRUCTURE so the list of candidate advisors is empty and no proxy is created. If you create the equivalent AOP bean configuration in resources.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

   <bean id="theAspect" class="TheAspect"/>
   <bean id="anotherAspect" class="apackage.AnotherAspect"/>

   <aop:config proxy-target-class="true">

      <aop:aspect ref="theAspect" id='theAspectDef'>
         <aop:around pointcut="execution(* TheServiceInterface.service(..))" method="invoke"/>
      </aop:aspect>

      <aop:aspect ref="anotherAspect" id='anotherAspectDef'>
         <aop:around pointcut="execution(* *.AnotherServiceInterface.service(..))" method="invoke"/>
      </aop:aspect>

   </aop:config>

</beans>
then it works fine because the auto proxy creator is an AspectJAwareAdvisorAutoProxyCreator with this implementation of isEligibleAdvisorBean() (from AbstractAdvisorAutoProxyCreator):
protected boolean isEligibleAdvisorBean(String beanName) {
   return true;
}
Hide
Graeme Rocher added a comment -

Thanks for the investigative work Burt. I'll look at changing GroovyAwareInfrastructureAdvisorAutoProxyCreator

Show
Graeme Rocher added a comment - Thanks for the investigative work Burt. I'll look at changing GroovyAwareInfrastructureAdvisorAutoProxyCreator

People

Vote (1)
Watch (4)

Dates

  • Created:
    Updated:
    Resolved: