Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [aspectj-users] How to resolve real execution signature for call joinpoint

Actually and unfortunately, contrary to your assumption
Signature.getDeclaringType() does not yield the same type as
thisJoinPoint.getTarget().getClass().

Here is some sample code (also attached) demonstrating the problem. You need
Apache Commons Lang (my version is 3.3.2) in order to use ClassUtils because
I found it too tedious to deal with Class.forName(<primitive type>) by
myself:


package de.scrum_master.app;

import java.util.ArrayList;
import java.util.List;

public class MyList<E> extends ArrayList<E> {
	private static final long serialVersionUID = -8827657434447203955L;

	public MyList() { super(); }
	@Override public boolean remove(Object o) { return false; }

	public void myMethod() {}
	public int myOtherMethod(int number) { return number * number; }

	public static void main(String[] args) {
		MyList<String> myList = new MyList<>();
		myList.add("foo");
		myList.add("bar");
		myList.remove("bar");
		myList.add(0, "XXX");
		for (String element : myList)
			System.out.println(element);
		myList.myMethod();
		myList.myOtherMethod(11);

		List<String> jdkList = new MyList<>();
		jdkList.add("one");
		jdkList.add("two");
		jdkList.remove("two");
		jdkList.add(0, "XXX");
		for (String element : jdkList)
			System.out.println(element);
	}
}


package de.scrum_master.aspect;

import java.lang.reflect.Method;

import org.apache.commons.lang3.ClassUtils;
import org.aspectj.lang.Signature;
import org.aspectj.lang.SoftException;

public aspect MethodTracer pertarget(call(* *(..))) {
	public MethodTracer() {
		super();
		System.out.printf(
				"%n%-60s  |  %-30s  |  %-30s  |  %-30s%n",
				"Join point",
				"AJ target class",
				"AJ method declaring type",
				"Real method declaring type"
			);
		System.out.printf(
				"%-60s==|==%-30s==|==%-30s==|==%-30s%n",
				"============================================================",
				"==============================",
				"==============================",
				"=============================="
			);
	}

	before(Object target) : !within(MethodTracer) && call(* *(..)) && target(target) {
		Signature methodSignature = thisJoinPointStaticPart.getSignature();
		Method method;
		try {
			String[] signatureParts = methodSignature.toLongString().split("[()]|, ");
			Class<?>[] parameterTypes = new Class<?>[signatureParts.length - 1];
			int index = -1;
			for (String signaturePart : signatureParts) {
				if (++index == 0)
					continue;
				parameterTypes[index - 1] = ClassUtils.getClass(signaturePart);
			}
			method = target.getClass().getMethod(methodSignature.getName(), parameterTypes);
		} catch (ClassNotFoundException|NoSuchMethodException e) {
			throw new SoftException(e);
		}
		System.out.printf(
			"%-60s  |  %-30s  |  %-30s  |  %-30s%n",
			thisJoinPointStaticPart,
			target.getClass().getName(),
			methodSignature.getDeclaringTypeName(),
			method.getDeclaringClass().getName()
		);
	}
}


The output looks like this for the two lists I create in the driver class:


Join point                                                    |  AJ target class                 |  AJ method declaring type        |  Real method declaring type    
==============================================================|==================================|==================================|================================
call(boolean de.scrum_master.app.MyList.add(Object))          |  de.scrum_master.app.MyList      |  de.scrum_master.app.MyList      |  java.util.ArrayList           
call(boolean de.scrum_master.app.MyList.add(Object))          |  de.scrum_master.app.MyList      |  de.scrum_master.app.MyList      |  java.util.ArrayList           
call(boolean de.scrum_master.app.MyList.remove(Object))       |  de.scrum_master.app.MyList      |  de.scrum_master.app.MyList      |  de.scrum_master.app.MyList    
call(void de.scrum_master.app.MyList.add(int, Object))        |  de.scrum_master.app.MyList      |  de.scrum_master.app.MyList      |  java.util.ArrayList           
call(Iterator de.scrum_master.app.MyList.iterator())          |  de.scrum_master.app.MyList      |  de.scrum_master.app.MyList      |  java.util.ArrayList           
XXX
foo
bar
call(void de.scrum_master.app.MyList.myMethod())              |  de.scrum_master.app.MyList      |  de.scrum_master.app.MyList      |  de.scrum_master.app.MyList    
call(int de.scrum_master.app.MyList.myOtherMethod(int))       |  de.scrum_master.app.MyList      |  de.scrum_master.app.MyList      |  de.scrum_master.app.MyList    

Join point                                                    |  AJ target class                 |  AJ method declaring type        |  Real method declaring type    
==============================================================|==================================|==================================|================================
call(boolean java.util.List.add(Object))                      |  de.scrum_master.app.MyList      |  java.util.List                  |  java.util.ArrayList           
call(boolean java.util.List.add(Object))                      |  de.scrum_master.app.MyList      |  java.util.List                  |  java.util.ArrayList           
call(boolean java.util.List.remove(Object))                   |  de.scrum_master.app.MyList      |  java.util.List                  |  de.scrum_master.app.MyList    
call(void java.util.List.add(int, Object))                    |  de.scrum_master.app.MyList      |  java.util.List                  |  java.util.ArrayList           
call(Iterator java.util.List.iterator())                      |  de.scrum_master.app.MyList      |  java.util.List                  |  java.util.ArrayList           
XXX
one
two

--
Alexander Kriegisch
http://scrum-master.de


Andy Clement schrieb am 23.05.2014 02:51:

> I had a further thought about this problem whilst out on a run, I can
> imagine (although I haven't coded it up) something around and if() clause
> and using reflection with <target>.class.getDeclaredMethod(<thisJoinPoint
> stuff in here>) (and walking up the hierarchy) to check where the method
> is actually declared. Won't do much for the performance though :)
>
>
> > As for Signature.getDeclaringTypeName(), the acceptance criteria do not
> seem to involve dynamic method resolution, only static code analysis (what
> is the target's declared type?). Would it be possible to enhance that
> method, add a toggle or similarly named method for dynamic resolution?
>
>
> Currently that information is built based solely on the invoke instruction
> (static info), we're not actually looking at what is on the stack so are
> at the mercy of what the compiler decided to do. It is feasible to add a
> similar named method but isn't that just the same as asking
> thisJoinPoint.getTarget().getClass() (at least for non static methods)?
>
>
> On 22 May 2014 16:06, Alexander Kriegisch <alexander@xxxxxxxxxxxxxx
> <mailto:alexander@xxxxxxxxxxxxxx> > wrote:
>>
>> An afterthought: As for Signature.getDeclaringTypeName(), the acceptance
>> criteria do not seem to involve dynamic method resolution, only static
>> code analysis (what is the target's declared type?). Would it be possible
>> to enhance that method, add a toggle or similarly named method for
>> dynamic resolution?
>>
>>
>> Alexander Kriegisch schrieb am 23.05.2014 00:55:
>>
>> > Okay, so you had the same thoughts ('if' pointcut and stacktrace check)
>> as I
>> > and also the same results. We would need something like a sneek peek
>> towards
>> > method resolution which happens just an instant later, but not quite at
>> the
>> > call site.
>> >
>> > BTW, this was one of my experimental pointcuts:
>> >
>> > pointcut jdkType() :
>> >
>> if(thisJoinPointStaticPart.getSignature().getDeclaringTypeName().startsWith("java."));
>> >
>> > But for the reasons I mentioned on StackOverflow this does not (and
>> cannot)
>> > work reliably for two out of four cases.
>> >
>> > Thank you anyway :-)
>> >
>> >
>> > Andy Clement schrieb am 23.05.2014 00:48:
>> >
>> >> I can't immediately think of a way to do that. Even if using an if()
>> clause on
>> >> the point cut to insert a runtime test, that test can't tell whose
>> method is
>> >> running on the object you have (whether it is a local one or an
>> inherited
>> >> one). You can't even inspect the stack trace in the advice (which
>> would be
>> >> crude anyway) because the advice invocation is made at the call site
>> before
>> >> you enter the method in question.
>> >>
>> >> Incidentally I am probably going to be hanging around on stack
>> overflow more
>> >> these days so anyone posting questions here, feel free to start
>> posting there,
>> >> I will see them :)
>> >>
>> >>
>> >> On 22 May 2014 00:59, Alexander Kriegisch wrote:
>> >>
>> >>> On StackOverflow I saw an interesting question. Even though I (user
>> >>> kriegaex) have answered it as good as I could at
>> >>>
>> http://stackoverflow.com/questions/23791760/aspectj-separating-native-library-calls-from-application-calls/23799457#23799457
>> >>> ,
>> >>> I am still wondering if there might be a way to find out which method
>> a
>> >>> call really resolves to later in all of the four cases mentioned
>> there,
>> >>> because in general a call joinpoint's signature is not necessarily
>> equal
>> >>> to what gets executed later.
>> >>>
>> >>> I don't know if anyone can answer that, but my best guess would be
>> Andy
>> >>> Clement (as usual). ;-)

Attachment: src.zip
Description: Zip archive


Back to the top