Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [aspectj-users] Getting the method arguments of a Pointcut using args

What you are implementing is called a wormhole pattern, only in your case you want to generalise it with respect to the caller arguments. For that you would need a hypothetical pointcut designator allArgs(myParameter) which does not exist and which would bind the whole Object[] as produced by Pointcut.getArgs() to a pointcut or advice parameter with a corresponding type. I admit it would be nice to have. If you believe you need it and cannot live with a workaround, feel free to create a Bugzilla ticket for it for future consideration. I cannot speak for Andy Clement, so I have no idea if he would consider implementing it or if he even has free cycles to do so.

As you said, you could use a singleton aspect with ThreadLocal variable in order to achieve what you want. As an alternative, I want to show you how you can do it with percflow instantiation. Which alternative you choose will depend on how many aspect instances would have to be created in your case and if that would become a problem or not. I recommend to just give it a try:

 

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import de.scrum_master.app.CallerAnnotation;

@Aspect("percflow(execution(* *(..)) && @annotation(de.scrum_master.app.CallerAnnotation))")
public class AnotherAspect {
private CallerAnnotation callerAnnotation;
private Object[] callerArgs;

@Before("execution(* *(..)) && @annotation(callerAnnotation)")
public void executeCaller(JoinPoint joinPoint, CallerAnnotation callerAnnotation) {
this.callerAnnotation = callerAnnotation;
callerArgs = joinPoint.getArgs();
}

@Around("execution(public void callee(String, Integer))")
public Object executeCallee(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println(joinPoint);
System.out.println(" Caller annotation: " + callerAnnotation);
System.out.println(" Caller arguments:");
for (Object arg : callerArgs)
System.out.println(" " + arg);
System.out.println(" Callee arguments:");
for (Object arg : joinPoint.getArgs())
System.out.println(" " + arg);
return joinPoint.proceed();
}
}


The console log for my previously posted sample code if you only replace the aspect:

execution(void de.scrum_master.app.SampleClazz.callee(String, Integer))
Caller annotation: @de.scrum_master.app.CallerAnnotation(value="xyz")
Caller arguments:
de.scrum_master.app.Foo@527740a2
de.scrum_master.app.Bar@13a5fe33
Callee arguments:
x
11
execution(void de.scrum_master.app.SampleClazz.callee(String, Integer))
Caller annotation: @de.scrum_master.app.CallerAnnotation(value="abc")
Caller arguments:
22
Callee arguments:
y
22
 
Kind regards
--
Alexander Kriegisch
https://scrum-master.de
 

Sina Golesorkhi schrieb am 07.09.2019 19:31:

Hi Alexander, 
 
Thanks for response. Please find my comments below.

On Sep 5, 2019, at 5:30 AM, Alexander Kriegisch <alexander@xxxxxxxxxxxxxx> wrote:

Hi Sina.

Your request sounds a bit like ordering Mousse au Chocolat without chocolate in a restaurant.

Interesting metaphor but I don’t see it being the case here :) 

Your advice has two very specific Foo and Bar parameters, but the corresponding pointcut should not bind them to method parameters. Where else would they come from then? What is the problem anyway? Please give me a reason for wishing to avoid making the pointcut specific to the method signature, then maybe I can help you find a solution for your problem.

 
 
The idea is to be able to annotate a method to tag it so that you can define an Aspect in which you extract some information from the annotation and the annotated method, hence @CallerAnnotation * *(..)).  The reason that I’m using * * (..) is because the I don’t want the Aspect to become aware of signature of the annotated method as this won’t scale if I’m going to use the annotation on top of different methods with different signatures. 
 
If I have to bind the the Pointcut definition to a specific method signature then I’m losing the generalization that I get through the annotation. If I only had one PointCut and my final JoinPoint was the annotated method then I could have easily used the thisJoinPoint.getArgs() in order to get the method arguments but since I’m using cflowbelow and combining that Pointcut with another Pointcut to catch the JoinPoint of the callee() method I can only access the args()  of the callee method but not the annotated method. 
 
I’m afraid that this is not possible only using AspectJ. My expectation was to be able to get an Object[] which contains the arguments of the annotated method without the need to specify its signature. 
One way that I can think of would be to have two different advices one for the annotated method and another one for the callee method and use ThreadLocal to pass on the information (arguments of annotated method) and examine and read those args in the second advice from ThreadLocal.
 
Thanks.  

Just guessing you want to re-use the method execution pointcut for another advice, but this time more generically without specific parameters, you can just do it like this (full MCVE follows):

 

package de.scrum_master.app;

public class Foo {}



package de.scrum_master.app;

public class Bar {}



package de.scrum_master.app;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(METHOD)
public @interface CallerAnnotation {
String value();
}



package de.scrum_master.app;

public class SampleClazz {
@CallerAnnotation("xyz")
public void caller(Foo foo, Bar bar) {
callee("x", 11);
}

@CallerAnnotation("abc")
public void anotherCaller(int number) {
callee("y", number);
}

public void callee(String s, Integer i) {}

public static void main(String[] args) {
SampleClazz sampleClazz = new SampleClazz();
sampleClazz.caller(new Foo(), new Bar());
sampleClazz.anotherCaller(22);
}
}



package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import de.scrum_master.app.Bar;
import de.scrum_master.app.CallerAnnotation;
import de.scrum_master.app.Foo;

@Aspect
public class MyAspect {
@Pointcut("execution(* *(..)) && @annotation(callerAnnotation)")
protected void executeAnnotation(CallerAnnotation callerAnnotation) {}

@Pointcut("executeAnnotation(callerAnnotation) && args(foo, bar)")
protected void executeAnnotationWithArgs(CallerAnnotation callerAnnotation, Foo foo, Bar bar) {}

@Pointcut("cflowbelow(executeAnnotation(callerAnnotation))")
protected void executeBelowAnnotation(CallerAnnotation callerAnnotation) {}

@Pointcut("cflowbelow(executeAnnotationWithArgs(callerAnnotation, foo, bar))")
protected void executeBelowAnnotationWithArgs(CallerAnnotation callerAnnotation, Foo foo, Bar bar) {}

@Pointcut(value = "execution(public void callee(String, Integer))")
protected void executeCallee() {}

@Around("executeCallee() && executeBelowAnnotationWithArgs(callerAnnotation, foo, bar)")
public Object finalAdvice(ProceedingJoinPoint joinPoint, CallerAnnotation callerAnnotation, Foo foo, Bar bar) throws Throwable {
System.out.println("[finalAdvice] " + joinPoint);
System.out.println(" " + callerAnnotation);
System.out.println(" " + foo);
System.out.println(" " + bar);
return joinPoint.proceed();
}

@Before("executeCallee() && executeBelowAnnotation(callerAnnotation)")
public void anotherAdvice(JoinPoint joinPoint, CallerAnnotation callerAnnotation) throws Throwable {
System.out.println("[anotherAdvice] " + joinPoint);
System.out.println(" " + callerAnnotation);
}
}
 
See how executeAnnotation(callerAnnotation) is re-used? Maybe this is what you want, maybe not, I have no idea.
 
BTW, the console log looks like this:
 
[finalAdvice] execution(void de.scrum_master.app.SampleClazz.callee(String, Integer))
@de.scrum_master.app.CallerAnnotation(value="xyz")
de.scrum_master.app.Foo@370736d9
de.scrum_master.app.Bar@5f9d02cb
[anotherAdvice] execution(void de.scrum_master.app.SampleClazz.callee(String, Integer))
@de.scrum_master.app.CallerAnnotation(value="xyz")
[anotherAdvice] execution(void de.scrum_master.app.SampleClazz.callee(String, Integer))
@de.scrum_master.app.CallerAnnotation(value="abc")
 
Regards
-- 
Alexander Kriegisch
https://scrum-master.de
 

Sina Golesorkhi schrieb am 05.09.2019 01:45:

Hi there I have the following code sample 
 
@Aspect
public class MyAspect {
 
    @Pointcut("cflowbelow(execution(@CallerAnnotation * *(..)) && @annotation(callerAnnotation))")
    protected void executeBelowAnnotation(final CallerAnnotation callerAnnotation) {
    }
 
    @Pointcut(value = "execution(public void callee(String, Integer))")
    protected void executeCallee() {
    }
 
    @Around("executeCallee() && executeBelowAnnotation(callerAnnotation)")
    public Object finalAdvice(final ProceedingJoinPoint joinPointfinal CallerAnnotation callerAnnotation)
            throws Throwable {
        return joinPoint.proceed();
    }
}
 
 
public class SampleClazz() {
 
    @CallerAnnotation("xyz")
    public void caller(Foo foo, Bar bar){
        this.callee();
    }
 
    private void callee(String s, Integer i){
      // Do Something
    }
}
 
and I would like to know is there any possibility to use the args such as args(..) in order to get the method arguments of the caller method in the following example?
 
I know that I can in my first pointcut define 
 
@Pointcut("cflowbelow(call(@CallerAnnotation * *(..)) && @annotation(callerAnnotation) && args(foo, bar))")
    protected void executeBelowAnnotation(final CallerAnnotation callerAnnotation, Foo foo, Bar bar) {
    }
 
So that I also pass on those method params to the finalAdvice method, however I would like to do this without explicitly defining the arguments in my pointcut so that it doesn’t depend on the signature of the annotated method. 
 
One way that I have done before has been using another annotation to annotate the params and then having @annotation in my pointcut but I would like to see whether I can skip having yet another annotation for this. 
 
Also another way might be the use of executionAdvice and then defining two advices and combining them together for the two pointcuts. That way I guess I can use the getMethodArgs() from ProceedingJoinPoint.
 
So is there any way to use the args() to achieve this? 
 
Regards
Sina
 
 
 
_______________________________________________
aspectj-users mailing list
aspectj-users@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://www.eclipse.org/mailman/listinfo/aspectj-users

Back to the top