Announcement Announcement Module
Collapse
No announcement yet.
One advice changes method arguments. 2nd advice cannot see it. Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • One advice changes method arguments. 2nd advice cannot see it.

    I have one advice which changes the method argument. I have another advice which just prints the argument. The first advice successfully changes the method arguments, but the second advice does not see the changed argument. It only sees the original arguments. Is it possible for the second advice to see the argument changes made by first advice?

    I have @Order on both aspects so that the first advice executes before the second advice. I am using Spring with AspectJ LTW.

    Here is the code:

    the bean which is advised:

    Code:
    package com.test;
    
    public class MyBean {	
    	
    	public void update(String str) {
    		updateStr(str);		
    	}
    	
    	@UpdatePrint
    	@ReplaceString
    	private void updateStr(String str) {		
    		doSomethingwithStr(str);
    	}
    
    	private void doSomethingwithStr(String str) {
    		System.out.println("doSomethingwithStr: " + str);		
    	}	
    }
    The first annotation and it's advice:
    Code:
    //ReplaceString.java
    package com.test;
    
    import java.lang.annotation.Target;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface ReplaceString {
    		
    }
    
    //ReplaceStringAspect.java
    package com.test;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.core.annotation.Order;
    
    @Aspect
    @Order(1)
    public class ReplaceStringAspect {
    	@Around("execution(* *(..)) && @annotation(ReplaceString)")
    	public Object logPrint(ProceedingJoinPoint pjp) throws Throwable{
    		Object[] args = pjp.getArgs();
    		String str = (String) args[0];
    		args[0] = str + "TWO.";	
    		
    		System.out.println("## Start ReplaceStringAspect. str=" + args[0]);
    		Object retVal = pjp.proceed(args);
    		System.out.println("## Done ReplaceStringAspect. str=" + args[0]);		
    		
    		return retVal;
    	}
    }
    This is the second annotation and it's advice:
    Code:
    //UpdatePrint.java
    package com.test;
    
    import java.lang.annotation.Target;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface UpdatePrint {
    		
    }
    
    
    //UpdatePrintAspect.java
    package com.test;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.core.annotation.Order;
    
    @Aspect
    @Order(1)
    public class UpdatePrintAspect {
    	@Around("execution(* *(..)) && @annotation(UpdatePrint)")
    	public Object logPrint(ProceedingJoinPoint pjp) throws Throwable{
    		String str = (String) pjp.getArgs()[0];
    		
    		System.out.println("<<<< Starting doSomethingwithStr with str=" + str);		
    		Object retVal = pjp.proceed();		
    		System.out.println("<<<< Done doSomethingwithStr with str=" + str);		
    		
    		return retVal;
    	}
    }
    This is the beans file:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:lang="http://www.springframework.org/schema/lang"
    	xmlns:aop="http://www.springframework.org/schema/aop"
    	xmlns:tx="http://www.springframework.org/schema/tx"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
    		http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    		http://www.springframework.org/schema/lang
    		http://www.springframework.org/schema/lang/spring-lang-2.5.xsd	
    		http://www.springframework.org/schema/aop
    		http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
    		http://www.springframework.org/schema/context
    		http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    	<!-- this switches on the load-time weaving -->
        <context:load-time-weaver aspectj-weaving="on"/>
    	   
        
        <bean id="myBean"
              class="com.test.MyBean"/>
       
    </beans>

    This is the META-INF/aop.xml file:
    Code:
    <aspectj>
        
        <aspects>        
            <aspect name="com.test.UpdatePrintAspect"/>
            <aspect name="com.test.ReplaceStringAspect"/>
        </aspects>
    	
       
        <weaver options="-verbose">
            <include within="com.test.MyBean"/>
        </weaver>
    
    </aspectj>
    This is the main class:
    Code:
    package com.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.FileSystemXmlApplicationContext;
    
    public final class Main {
    
        public static void main(String[] args) {
    
            ApplicationContext ctx = new FileSystemXmlApplicationContext("src/beans.xml");
    
            MyBean bean = (MyBean) ctx.getBean("myBean");
    
            bean.update("ONE.");
           
        }
    }
    This is the output. You can see that the the first advise changes the arguments successfully, but the second advice does not see the changed argument:
    Code:
    Dec 28, 2009 5:05:21 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
    INFO: Refreshing org.springframework.context.support.FileSystemXmlApplicationContext@1608e05: display name [org.springframework.context.support.FileSystemXmlApplicationContext@1608e05]; startup date [Mon Dec 28 17:05:21 EST 2009]; root of context hierarchy
    Dec 28, 2009 5:05:21 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    INFO: Loading XML bean definitions from file [C:\eclipse_workspace\SpringAspectJ2\src\beans.xml]
    Dec 28, 2009 5:05:21 PM org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
    INFO: Bean factory for application context [org.springframework.context.support.FileSystemXmlApplicationContext@1608e05]: org.springframework.beans.factory.support.DefaultListableBeanFactory@65a77f
    Dec 28, 2009 5:05:21 PM org.springframework.context.weaving.DefaultContextLoadTimeWeaver setBeanClassLoader
    INFO: Found Spring's JVM agent for instrumentation
    [AppClassLoader@130c19b] info AspectJ Weaver Version 1.6.6 built on Wednesday Sep 30, 2009 at 18:55:14 GMT
    [AppClassLoader@130c19b] info register classloader sun.misc.Launcher$AppClassLoader@130c19b
    [AppClassLoader@130c19b] info using configuration /C:/eclipse_workspace/SpringAspectJ2/lib/META-INF/aop.xml
    [AppClassLoader@130c19b] info register aspect com.test.UpdatePrintAspect
    [AppClassLoader@130c19b] info register aspect com.test.ReplaceStringAspect
    Dec 28, 2009 5:05:22 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
    INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@65a77f: defining beans [org.springframework.context.weaving.AspectJWeavingEnabler#0,loadTimeWeaver,myBean]; root of factory hierarchy
    ## Start ReplaceStringAspect. str=ONE.TWO.
    <<<< Starting doSomethingwithStr with str=ONE.
    doSomethingwithStr: ONE.TWO.
    <<<< Done doSomethingwithStr with str=ONE.
    ## Done ReplaceStringAspect. str=ONE.TWO.

  • #2
    Is this at all possible?

    Comment


    • #3
      The @Order annotation has no effect when using the AspectJ weaver. Instead, take a look at "declare precedence" and @DeclarePrecedence. Furthermore, there are rules governing precedence between advice from the same aspect.

      -Ramnivas

      Comment


      • #4
        Thanks Ramnivas.

        I tried using @DeclarePrecedence, but still gives the same output.

        The basic question here is that - is it at all possible for the second advice to see the modified arguments by the first advice?

        Comment


        • #5
          Hey,

          Yes, AspectJ can do this - it requires precedence to behave reliably, as Ramnivas says:

          Code:
          public class AA {
          	public static void main(String[] args) {
          		new AA().m(5);
          	}
          	
          	public void m(int i) {
          		
          	}
          }
          
          aspect A {
          
          	before(int a): execution(* m(..)) && args(a) {
          		System.out.println("in before advice, arg is "+a);
          	}
          }
          
          aspect B {
          	declare precedence: B, *;
          	void around(int a): execution(* m(..)) && args(a) {
          		System.out.println("in around advice, arg is "+a);
          		proceed(a*2);
          	}
          	
          }
          Code:
          java AA
          in around advice, arg is 5
          in before advice, arg is 10
          without precedence specified:

          Code:
          java AA
          in before advice, arg is 5
          in around advice, arg is 5
          Unfortunately I'm not quite expert enough to define that in spring for you, but if you really can't get it setup I'll try and find out how.

          cheers,
          Andy
          ---
          Andy Clement
          AspectJ Lead

          Comment


          • #6
            Thanks Andy. That helps.
            I didnt find much documentation on the usage of @DeclarePrecedence in spring. I tried the following and it didnt work.

            Code:
            @Aspect
            @DeclarePrecedence("(@ReplaceString *), *")
            public class UpdatePrintAspect {
            	@Around("execution(* *(..)) && @annotation(UpdatePrint)")
            	public Object logPrint(ProceedingJoinPoint pjp) throws Throwable{
            		String str = (String) pjp.getArgs()[0];
            		
            		System.out.println("<<<< Starting doSomethingwithStr with str=" + str);		
            		Object retVal = pjp.proceed();		
            		System.out.println("<<<< Done doSomethingwithStr with str=" + str);		
            		
            		return retVal;
            	}
            	
            	
            }
            Maybe I am not declaring it correctly. Any ideas?

            Comment


            • #7
              The precedence specifies a partial ordering amongst the aspects, not the affected targets.

              @DeclarePrecedence("ReplaceStringAspect,*")

              if that doesn't work I'll put together a spring sample that does.

              cheers,
              Andy

              Comment


              • #8
                doesnt work. A spring example will be of immense help. Thanks a lot Andy

                Comment


                • #9
                  Right. I setup your example as a Spring project, I changed the logging messages a little just for my own benefit. When I ran it first time I saw:

                  Code:
                  >> UpdatePrintAspect: str=ONE.
                  >> ReplaceStringAspect: str=ONE.TWO.
                  doSomethingwithStr: ONE.TWO.
                  >> ReplaceStringAspect: str=ONE.TWO.
                  << UpdatePrintAspect: str=ONE.
                  I then put a Declare Precedence in the aop.xml to switch that order:

                  Code:
                   <aspects>        
                          <aspect name="com.test.UpdatePrintAspect"/>
                          <aspect name="com.test.ReplaceStringAspect"/>
                           <concrete-aspect name="mypack.__MyDeclarePrecedence"
                                                           precedence="*..*ReplaceStringAspect, *"/>
                      </aspects>
                  And the output changes to

                  Code:
                  >> ReplaceStringAspect: str=ONE.TWO.
                  >> UpdatePrintAspect: str=ONE.
                  doSomethingwithStr: ONE.TWO.
                  << UpdatePrintAspect: str=ONE.
                  >> ReplaceStringAspect: str=ONE.TWO.
                  and still the UpdatePrintAspect is not seeing the replaced string. I then observed that you are not binding the parameter value in the UpdatePrintAspect, you are fetching it from the joinpoint object. At the joinpoint the value is unchanged - only in the advice chain is it changed. So I changed your UpdatePrintAspect from:

                  Code:
                  @Around("execution(* *(..)) && @annotation(UpdatePrint)")
                  public Object logPrint(ProceedingJoinPoint pjp) throws Throwable{
                  	String str = (String) pjp.getArgs()[0];
                  to

                  Code:
                  @Around("execution(* *(..)) && @annotation(UpdatePrint) && args(str)")
                  public Object logPrint(ProceedingJoinPoint pjp, String str) throws Throwable{
                  And then ran it once more:

                  Code:
                  >> ReplaceStringAspect: str=ONE.TWO.
                  >> UpdatePrintAspect: str=ONE.TWO.
                  doSomethingwithStr: ONE.TWO.
                  << UpdatePrintAspect: str=ONE.TWO.
                  >> ReplaceStringAspect: str=ONE.TWO.
                  UpdatePrintAspect sees the change made.

                  Andy

                  Comment


                  • #10
                    works!!

                    great. that works!!!
                    Thanks a lot Andy. Really appreciate you spending time on this..

                    Comment

                    Working...
                    X