/*
We are using Aspect to do AOP on some existing application and we also used threadlocal to store GUId. we are using #Around annotation.
At the start of the transaction we are setting the GUID in transaction with initialValue() method.
Issue is as we know when we are using threadlocal we should also take care about removing the data from threadlocal otherwise it may result i outofmemory execption. if i am removing it at
the last of the aspect it is corrupting the code and changing the UUID value.
Please suggest how we can achieve it without outofmemory.
Code :-
*/
#Aspect
public class DemoAspect {
#Pointcut("execution(* *.*(..)) ")
public void logging() {}
private static ThreadLocal<String> id = new ThreadLocal<String>() {
#Override
protected String initialValue(){
return UUID.randomUUID().toString();
}
};
#Around("logging()")
public Object tracing(ProceedingJoinPoint thisJoinPoint) throws Throwable {
String methodSignature=thisJoinPoint.getSignature().toString();
if(id.get().toString()==null || id.get().toString().length()==0)
id.set(UUID.randomUUID().toString());
System.out.println("Entering into "+methodSignature);
Object ret = thisJoinPoint.proceed();
System.out.println(id.get().toString());
System.out.println("Exiting into "+methodSignature);
//id.remove();
return ret;
}
}
Before we start a little hint: If you write #Around("logging()") your pointcut method should be renamed from loggingResponseTime() to actually logging(), otherwise the aspect will not work.
Now as for your real problem: You are making a typical beginners' mistake by advising code too broadly, i.e. you are intercepting all method executions (outside the JDK). If you use Eclipse and AJDT and you put your cursor into the tracing() advice you will see something like this in the AspectJ "cross reference" window using your current pointcut:
You can immediately see your problem: Your pointcut captures code in your anonymous ThreadLocal subclass. This leads to endless recursion and finally to the StackOverflowError as you can see in your own callstack if you inspect it.
Now here is some sample code demonstrating the problem for other people's reference:
Driver application:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
System.out.println(bar(foo()));
}
public static String bar(String text) {
return text + "bar";
}
private static String foo() {
return "foo";
}
}
Aspect:
package de.scrum_master.aspect;
import java.util.UUID;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class DemoAspect {
private static ThreadLocal<String> id = new ThreadLocal<String>() {
#Override
protected String initialValue() {
return UUID.randomUUID().toString();
}
};
#Pointcut("execution(* *(..))")
public void logging() {}
#Around("logging()")
public Object tracing(ProceedingJoinPoint thisJoinPoint) throws Throwable {
String methodSignature = thisJoinPoint.getSignature().toString();
if (id.get().toString() == null || id.get().toString().length() == 0)
id.set(UUID.randomUUID().toString());
System.out.println("Entering into " + methodSignature);
Object ret = thisJoinPoint.proceed();
System.out.println(id.get().toString());
System.out.println("Exiting from " + methodSignature);
id.remove();
return ret;
}
}
Console output:
Exception in thread "main" java.lang.StackOverflowError
at org.aspectj.runtime.reflect.SignatureImpl$CacheImpl.get(SignatureImpl.java:217)
at org.aspectj.runtime.reflect.SignatureImpl.toString(SignatureImpl.java:50)
at org.aspectj.runtime.reflect.SignatureImpl.toString(SignatureImpl.java:62)
at de.scrum_master.aspect.DemoAspect$1.initialValue_aroundBody1$advice(DemoAspect.aj:29)
at de.scrum_master.aspect.DemoAspect$1.initialValue(DemoAspect.aj:1)
at de.scrum_master.aspect.DemoAspect$1.initialValue(DemoAspect.aj:1)
at java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:160)
at java.lang.ThreadLocal.get(ThreadLocal.java:150)
at de.scrum_master.aspect.DemoAspect$1.initialValue_aroundBody1$advice(DemoAspect.aj:30)
at de.scrum_master.aspect.DemoAspect$1.initialValue(DemoAspect.aj:1)
at de.scrum_master.aspect.DemoAspect$1.initialValue(DemoAspect.aj:1)
at java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:160)
at java.lang.ThreadLocal.get(ThreadLocal.java:150)
(...)
So what can you do? It is actually quite simple: Just exclude the joinpoints you do not really want to intercept from your pointcut. For that you have several options. I am just naming a few:
A) Put your aspects into a specific package and exclude all (aspect) classes in that package:
#Pointcut("execution(* *(..)) && !within(de.scrum_master.aspect..*)")
B) Exclude all classes annotated by #Aspect:
#Pointcut("execution(* *(..)) && !within(#org.aspectj.lang.annotation.Aspect *)")
C) Exclude all (aspect) classes matching a certain naming scheme like *Aspect:
#Pointcut("execution(* *(..)) && !within(*..*Aspect)")
D) Exclude code from all ThreadLocal subclasses (+ syntax):
#Pointcut("execution(* *(..)) && !within(ThreadLocal+)")
In each case the result will be the same:
Entering into void de.scrum_master.app.Application.main(String[])
Entering into String de.scrum_master.app.Application.foo()
d2b83f5f-7282-4c06-9b81-6601c8e0499d
Exiting from String de.scrum_master.app.Application.foo()
Entering into String de.scrum_master.app.Application.bar(String)
0d1c9463-4bbd-427d-9d64-c7f3967756cf
Exiting from String de.scrum_master.app.Application.bar(String)
foobar
aa96bbbd-a1a1-450f-ae6e-77ab204c5fb2
Exiting from void de.scrum_master.app.Application.main(String[])
By the way: I have strong doubts about your usage of UUIDs because I see no value in creating expensive objects here. How about just logging timestamps? Why do you need globally unique IDs for logging? They tell you nothing. Furthermore, not only are you creating one ID per thread, but if you use the uncommented id.remove() you even create one per call! Sorry, but this is bloat, it slows down your code and creates lots of unnecessary objects. I do not think this is wise.
Update:
I forgot to explain the reason for the endless recursion: Your advice calls ThreadLocal.get(), assuming it could be null. Actually it cannot be because if the value has not been initialised, get() does so by utilising initialValue(). Even if you manually call remove(), the next time you call get() it will again initialise the value again and so forth. This is documented behaviour:
Returns the value in the current thread's copy of this thread-local variable. If the variable has no value for the current thread, it is first initialized to the value returned by an invocation of the initialValue() method.
So what happens, step by step?
A method is called.
Your around advice kicks in.
You call id.get() from the advice.
ThreadLocal.get() checks if a value is set, notices that there is none and calls your overridden initialValue() method.
Because the overridden initialValue() method is captured by your match-all pointcut execution(* *(..)), again your advice kicks in before the initial value has been set. The end result is that the loop starts again and so forth - endless recursion, quod erat demonstrandum.
So actually your problem boils down to calling get() on an uninitialised ThreadLocal subclass from an advice while simultaneously targetting its user-defined initialValue() method with the same advice. This is what creates the endless recursion and ultimately makes your stack overflow.
My recommendation is to exclude your aspect from the pointcut, see example pointcuts above. You should also get rid of the null check for the ThreadLocal value because it is superfluous. Last but not least, I assume you want one ThreadLocal value per thread and not one per method call. So you can do without any set() or remove() calls altogether.
Modified driver class, creating an additional thread:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
#Override
public void run() {
System.out.println(bar(foo()));
}
}).start();
Thread.sleep(200);
}
public static String bar(String text) {
return text + "bar";
}
private static String foo() {
return "foo";
}
}
Improved aspect:
package de.scrum_master.aspect;
import java.util.UUID;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class DemoAspect {
private static ThreadLocal<UUID> id = new ThreadLocal<UUID>() {
#Override
protected UUID initialValue() {
return UUID.randomUUID();
}
};
#Pointcut("execution(* *(..)) && !within(DemoAspect)")
public void logging() {}
#Around("logging()")
public Object tracing(ProceedingJoinPoint thisJoinPoint) throws Throwable {
Signature methodSignature = thisJoinPoint.getSignature();
System.out.println(
"Thread " + Thread.currentThread().getId() +
"[" + id.get() +
"] >>> " + methodSignature
);
Object result = thisJoinPoint.proceed();
System.out.println(
"Thread " + Thread.currentThread().getId() +
"[" + id.get() +
"] <<< " + methodSignature
);
return result;
}
}
Console output:
Thread 1[549d0856-0a92-4031-9331-a1317d6a43c4] >>> void de.scrum_master.app.Application.main(String[])
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] >>> void de.scrum_master.app.Application.1.run()
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] >>> String de.scrum_master.app.Application.access$0()
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] >>> String de.scrum_master.app.Application.foo()
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] <<< String de.scrum_master.app.Application.foo()
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] <<< String de.scrum_master.app.Application.access$0()
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] >>> String de.scrum_master.app.Application.bar(String)
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] <<< String de.scrum_master.app.Application.bar(String)
foobar
Thread 9[32c8444c-0f1f-4023-9b97-69d5beda3b4c] <<< void de.scrum_master.app.Application.1.run()
Thread 1[549d0856-0a92-4031-9331-a1317d6a43c4] <<< void de.scrum_master.app.Application.main(String[])
As you can see, threads already have unique IDs, so maybe you want consider implementing your aspect without any UUIDs altogether.
Related
I have these classes:
public class NegativeNumberException extends Exception{
NegativeNumberException()
{
System.out.println("Don't pass negative values!");
}
public String getMessage()
{
String message="NegativeNumberException";
return message;
}
}
public class Main {
static void test(int n) throws NegativeNumberException
{
if(n<0) throw new NegativeNumberException();
else System.out.println("Success");
}
public static void main(String[] args) {
try
{
test(-5);
}
catch(NegativeNumberException nne)
{
System.out.println(nne);
}
}
}
This is the result:
Dont pass negative values!
NegativeNumberException: NegativeNumberException
I expected it to print NegativeNumberException once, i guess if i override getMessage it gets executed no matter what, is it correct or something else is going on?
All objects have a toString() implementation. Because java.lang.Object itself (and Exception extends Throwable, and Throwable extends Object - all things eventually extend object) has this.
The Throwable class overrides the implementation of it with:
#Override public String toString() {
String s = getClass().getName();
String message = getLocalizedMessage();
return (message != null) ? (s + ": " + message) : s;
}
and getLocalizedMessage() is implemented as:
public String getLocalizedMessage() {
return getMessage();
}
whenever you 'append' an object to a string (with X + Y, where the X expression has type String and Y is anything) is shorthand for X.concat(Y.toString()). Thus, you're calling toString there. You did not override it, so you get Throwable's implementation of it, which invokes getLocalizedMessage - which you also didn't override, so that calls getMessage(), hence, you get your text. Twice.
Some lessons:
Do not System.out anything in exception constructors. The exception goes someplace and if it needs printing, where-ever it ends up will print it. If an exception ends up 'ending' your static void main(String[]) method, java will print it for you.
The message should not end in punctuation (and definitely not an exclamation mark - it's an exception. That something went wrong is assumed, no need to yell about it), and should not repeat or consist of the exception type. That information is already available; the message exists for additional information. If you have nothing to add, then don't have a message. An exception named NegativeNumberException should probably have the actual number as message, or possibly the param name. Something like:
public class NegativeNumberException extends Exception {
public NegativeNumberException(String msg) {
super(msg);
}
}
and then in your test method:
if (n < 0) throw new NegativeNumberException("n: " + n);
Overriding getMessage and returning some string constant is almost never correct. Messages should rarely be constants, as they are meant for detailing the specifics of the problem, not for generally explaining what the exception represents (write some javadoc for that, and only if the name of the exception is not sufficient to figure it out, hence, nothing needed here).
I'm new to unit testing, and I'm trying to test that a method has been called. The method in question doesn't return anything.
public void example (boolean foo) {
if (foo) {
processFoo(foo);
}
else if (foo==false) {
processSomethingElse(foo);
}
}
I want to be able to test that the processFoo method is being called, but I don't know how to do that.
If mocking is required, then I have to use JMockit. Thanks!
Sorry I'm a little late to the party, but I have a couple of ideas for you.
First, you mention that one option is to use JMockit--that's great as it gives you a lot of flexibility. If you use JMockit, then the visibility of your processFoo() method doesn't much matter. Let's see what that might look like:
public class Subject {
public void example (boolean foo) {
if (foo) {
processFoo(foo);
}
else if (foo==false) {
processSomethingElse(foo);
}
}
private void processFoo(boolean b) {
System.out.println("b = " + b);
}
private void processSomethingElse(boolean bb) {
System.out.println("bb = " + bb);
}
}
So, one caveat with this option, though is that I'm going to assume processFoo() is a method on your test subject and I'm going to use a partial mock to change the test subject--not something I really like to do, but this is an example. In general, it is best to only mock the dependencies of your test subject rather than behavior of the test subject itself--you have been advised! Note that the processFoo() method of the test subject is private. I'm going to substitute a method for the test with JMockit's partial mocking and the visibility of that new method does not have to match the original.
import static org.assertj.core.api.Assertions.assertThat;
import mockit.Mock;
import mockit.MockUp;
import mockit.integration.junit4.JMockit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
#RunWith(JMockit.class)
public class SubjectTest {
private Subject testSubject = new Subject();
private boolean processFooCalled = false;
#Before
public void setup() {
new MockUp<Subject>() {
#Mock
public void processFoo(boolean b) {
processFooCalled = true;
};
};
}
#Test
public void should_call_processFoo() {
testSubject.example(true);
assertThat(processFooCalled).isTrue();
}
#Test
public void should_not_call_processFoo() {
testSubject.example(false);
assertThat(processFooCalled).isFalse();
}
}
Ok, so that was the first option. It's actually a little easier if you forget JMockit for this one, assuming you are able to subclass your test subject and override the processFoo() method:
public class Subject {
public void example (boolean foo) {
if (foo) {
processFoo(foo);
}
else if (foo==false) {
processSomethingElse(foo);
}
}
protected void processFoo(boolean b) { // NOTE: protected access here!
System.out.println("b = " + b);
}
private void processSomethingElse(boolean bb) {
System.out.println("bb = " + bb);
}
}
So, in this case, the strategy is simply to subclass your test subject and replace the implementation of the method you wish to observe being called. It might look like this:
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
public class SubjectTest2 {
private Subject testSubject = new TestableSubject();
private boolean processFooCalled = false;
#Test
public void should_call_processFoo() {
testSubject.example(true);
assertThat(processFooCalled).isTrue();
}
#Test
public void should_not_call_processFoo() {
testSubject.example(false);
assertThat(processFooCalled).isFalse();
}
class TestableSubject extends Subject {
#Override
protected void processFoo(boolean b) {
processFooCalled = true;
}
}
}
Give it a whirl. Hope it helps!
You could use a counter variable in your class and increment it whenever the method is called, or use a print statement. If you don't have access to the processFoo method, a simple approach would be to do this at the time that processFoo is called in another method, if that's the only place where it can possibly be called.
For example:
public static int processFooCalls = 0;
// ...
public void example (boolean foo) {
if (foo) {
processFoo(foo);
processFooCalls += 1;
// and/or
System.out.println("processFoo method was called");
}
// ...
}
public static void main (String[] args) {
// main routine here...
System.out.println("'processFoo' was called " + processFooCalls + " times.");
}
If processFoo can be called elsewhere, and you need to consider this possibility as well, then you'll need to have access to the processFoo code in order to do this, e.g.:
void processFoo( boolean b ) {
// increment number of times processFoo was called here, and/or print, as follows
processFooCalls += 1;
System.out.println("called processFoo method!");
/* some functionality */
}
Looking at the JMockit documentation, you will need the following tools:
Static Mocking: http://jmockit.github.io/tutorial/BehaviorBasedTesting.html#staticPartial
Invocation Counts: http://jmockit.github.io/tutorial/BehaviorBasedTesting.html#constraints
Combining the two in a test (my syntax may be a little off since I'm more accustomed to Mockito, but the concept should hold):
#Test
public void someTestMethod(#Mocked({"processFoo"}) final ExampleClass exampleclass)
{
new Expectations() {{
exampleclass.processFoo(); times = 1;
}};
exampleclass.example(true);
}
This should mock the processFoo method, leaving everything else intact, and checks to make sure it is called exactly once.
Don't consider doing any kind of partial mocking for this, all you're doing in that case is ensuring that if you want to refactor your code your tests will fail. There is a mantra in unit testing - "never test private methods".
What you should be doing is testing that the method you call conforms to the behaviour you want to see. In this case what happens when foo is true is what's important, not that it calls processFoo. So if foo is true you want to be testing that the action processFoo carries out is true and nothing else.
I have already written AspectJ aspects that perform #Around advice triggered by method annotations. Now I want to do the same, but where fields are annotated instead of methods. So with each method invocation of the class below, it must set the accountSummary field to the correct implementation. Is there a way to accomplish this? I presume using #Before advice would be the best way of going about it. Using CDI is not an option - the solution must use AspectJ.
public class PoolableBusinessLogic {
#InjectServiceClientAdapter(legacy=LegacyAccountSummary.class,new=NewAccountSummary.class)
private AccountSummary accountSummary;
public void foo() {
// use correct accountSummary impl, decided in #Before code
}
public void bar() {
// use correct accountSummary impl, decided in #Before code
}
}
I am not sure what exactly you want to achieve, so I am presenting two alternative solutions.
First, let us create some application classes in order to have a fully compileable sample:
package de.scrum_master.app;
public interface AccountSummary {
void doSomething();
}
package de.scrum_master.app;
public class LegacyAccountSummary implements AccountSummary {
#Override
public void doSomething() {
System.out.println("I am " + this);
}
}
package de.scrum_master.app;
public class NewAccountSummary implements AccountSummary {
#Override
public void doSomething() {
System.out.println("I am " + this);
}
}
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectServiceClientAdapter {
Class<?> legacyImpl();
Class<?> newImpl();
}
package de.scrum_master.app;
public class PoolableBusinessLogic {
#InjectServiceClientAdapter(legacyImpl = LegacyAccountSummary.class, newImpl = NewAccountSummary.class)
private AccountSummary accountSummary;
public void foo() {
accountSummary.doSomething();
}
public void bar() {
System.out.println("Account summary is " + accountSummary);
}
}
Now we need an entry point:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
PoolableBusinessLogic businessLogic = new PoolableBusinessLogic();
businessLogic.foo();
businessLogic.bar();
System.out.println();
}
}
}
Obviously this yields an error because the member accountSummary has not been initialised:
Exception in thread "main" java.lang.NullPointerException
at de.scrum_master.app.PoolableBusinessLogic.foo(PoolableBusinessLogic.java:8)
at de.scrum_master.app.Application.main(Application.java:7)
Now we have two options, depending on what you want to achieve:
Option A: dynamic injection
Scenario: For each field access (even in the same PoolableBusinessLogic instance) decide dynamically what type of object instance to return. Here in this example I will just be randomising in order to simulate another if-else criterion.
BTW, I hope it is okay that I use the more expressive native AspectJ syntax. You can easily convert the aspect to annotation style.
package de.scrum_master.aspect;
import java.util.Random;
import org.aspectj.lang.SoftException;
import de.scrum_master.app.InjectServiceClientAdapter;
public aspect DynamicInjectionAspect {
private static final Random RANDOM = new Random();
Object around(InjectServiceClientAdapter adapterAnn) :
get(* *) && #annotation(adapterAnn)
{
try {
Class<?> implClass = RANDOM.nextBoolean() ? adapterAnn.legacyImpl() : adapterAnn.newImpl();
return implClass.newInstance();
} catch (Exception e) {
throw new SoftException(e);
}
}
}
This yields the following output:
I am de.scrum_master.app.LegacyAccountSummary#4d9cfefb
Account summary is de.scrum_master.app.NewAccountSummary#7e28388b
I am de.scrum_master.app.NewAccountSummary#2986e62
Account summary is de.scrum_master.app.LegacyAccountSummary#6576e542
I am de.scrum_master.app.NewAccountSummary#60c58418
Account summary is de.scrum_master.app.LegacyAccountSummary#4763754a
I am de.scrum_master.app.NewAccountSummary#52a971e3
Account summary is de.scrum_master.app.NewAccountSummary#7274187a
I am de.scrum_master.app.LegacyAccountSummary#23f32c4a
Account summary is de.scrum_master.app.LegacyAccountSummary#31e0c0b6
As you can see, within each of the five output groups (i.e. for each PoolableBusinessLogic instance) there are different account summary object IDs and sometimes (not always) even different class names.
Option B: static injection
Scenario: Per PoolableBusinessLogic instance decide dynamically what type of object instance to statically assign to the annotated member if its value is null. After that, do not overwrite the member anymore but return the previously initialised value. Again I will just be randomising in order to simulate another if-else criterion.
Attention: Do not forget to deactivate the first aspect, e.g. by prepending if(false) && to its pointcut. Otherwise the two aspects will be conflicting with each other.
package de.scrum_master.aspect;
import java.lang.reflect.Field;
import java.util.Random;
import org.aspectj.lang.SoftException;
import de.scrum_master.app.InjectServiceClientAdapter;
public aspect StaticInjectionAspect {
private static final Random RANDOM = new Random();
before(InjectServiceClientAdapter adapterAnn, Object targetObj) :
get(* *) && #annotation(adapterAnn) && target(targetObj)
{
try {
Field field = targetObj.getClass().getDeclaredField(thisJoinPoint.getSignature().getName());
field.setAccessible(true);
if (field.get(targetObj) != null)
return;
Class<?> implClass = RANDOM.nextBoolean() ? adapterAnn.legacyImpl() : adapterAnn.newImpl();
field.set(targetObj,implClass.newInstance());
} catch (Exception e) {
throw new SoftException(e);
}
}
}
This is a bit uglier because it involves using reflection for finding the member field. Because it might be (and in our example really is) private we need to make it accessible before doing anything with it.
This yields the following output:
I am de.scrum_master.app.NewAccountSummary#20d1fa4
Account summary is de.scrum_master.app.NewAccountSummary#20d1fa4
I am de.scrum_master.app.NewAccountSummary#2b984909
Account summary is de.scrum_master.app.NewAccountSummary#2b984909
I am de.scrum_master.app.LegacyAccountSummary#1ae3043b
Account summary is de.scrum_master.app.LegacyAccountSummary#1ae3043b
I am de.scrum_master.app.LegacyAccountSummary#2e2acb47
Account summary is de.scrum_master.app.LegacyAccountSummary#2e2acb47
I am de.scrum_master.app.LegacyAccountSummary#7b87b9fe
Account summary is de.scrum_master.app.LegacyAccountSummary#7b87b9fe
Now the output looks different: Within each of the five output groups (i.e. for each PoolableBusinessLogic instance) both output lines show exactly the same object ID.
For Option A: dynamic injection in kriegaex's answer, the annotation-style aspect will look like this:
#Aspect
public class InjectServiceClientAdapterAspect {
#Pointcut("get(* *) && #annotation(injectAnnotation)")
public void getServiceClientAdapter(InjectServiceClientAdapter injectAnnotation) {
}
#Around("getServiceClientAdapter(injectAnnotation)")
public Object injectServiceClientAdapter(final ProceedingJoinPoint joinPoint, final InjectServiceClientAdapter injectAnnotation) {
// injection code goes here
}
Given a class with a bunch of members, each with their own getter/setter/etc methods, is there a way to design a pointcut that will trigger only on members' methods when contained within the parent class?
For example:
public MyClass{
List myList = new ArrayList<String>();
}
If I want to create a pointcut to advise myList.add(), is there a way to do this? I do not wish to advise all ArrayList.add() calls. Only to Collections.add() that are members of MyClass.
I've tried playing around with within and cflow, but to no avail:
pointcut addPointcut() : cflow( execution( * *.getMyList() ) ) && call( * *.add(..));
but it does not seem to work. I presume that given that that the add() calls are not actually part of the get() control flow, it doesn't seem to trigger properly.
After some more playing around, I've noticed the following solution seems to work:
pointcut addPointcut(): within( MyClass ) && call( * *.add(..) );
Is this the correct implementation?
I've tried to limit the pointcut to only advise calls to add() when passing an #Entity object, but it does not work. Ex:
pointcut addEntityPointcut(): within( MyClass ) && call( * *.add(#javax.persistence.Entity *) );
and yet the addPointcut() works when called with an #Entity as a parameter.
Is the argument type based on the actual calling method, or based on the add() signature?
EDIT
I was too quick to jump to the wrong conclusion. After sleeping, I've come to recognize that my pointcut will not work.
public class FirstClass{
List<String> strings = new ArrayList<>();
// getters and setters
}
public class Execute{
public main(){
FirstClass fc = new FirstClass();
fc.getStrings().add( "This call is advised" ); // <---- Is there any way to advise this add() method?
List<String> l = new ArrayList<>();
l.add( "This call is not advised" ); // <---- this one should not be advised
}
}
I'm looking for a way to advise the add() method called from any class. However, I'm only looking to advise the add() method on the member List contained within FirstClass, even when called from outside FirstClass.
Is the argument type based on the actual calling method, or based on the add() signature?
In AspectJ for the call() pointcut you need to specify method or constructor signatures. The add() method in this case does not have any parameters annotated by #Entity, thus what you are trying to do does not work. This is a workaround using reflection:
Sample annotation:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface Entity {}
Sample entity:
package de.scrum_master.app;
#Entity
public class MyEntity {}
Driver application:
package de.scrum_master.app;
import java.util.ArrayList;
import java.util.List;
public class Application {
List<Object> myList = new ArrayList<>();
public static void main(String[] args) {
Application application = new Application();
application.myList.add("foo");
application.myList.add(new MyEntity());
application.myList.add("bar");
application.myList.add(new MyEntity());
}
}
Aspect:
package de.scrum_master.aspect;
import de.scrum_master.app.Application;
import de.scrum_master.app.Entity;
public aspect EntityAddInterceptor {
pointcut addEntity(Object addedObject) :
within(Application) && call(* *.add(*)) && args(addedObject);
before(Object addedObject) : addEntity(addedObject) {
if (addedObject.getClass().isAnnotationPresent(Entity.class))
System.out.println(thisJoinPointStaticPart + " -> " + addedObject);
}
}
Output:
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity#19dc6592
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity#54906181
As for the control flow matching variant, I think from the naming perspective it makes sense to assume that getMyList() does not add anything, but just return a list. Probably you rather do something like application.getMyList().add("foo"), and in this case the add() is really outside (after) the control flow of getMyList() because it operates on its result.
If OTOH you have a hypothetical method addToList(Object element) which really calls add() you can use cflow(). Let us modify the code sample:
Modified driver application:
package de.scrum_master.app;
import java.util.ArrayList;
import java.util.List;
public class Application {
List<Object> myList = new ArrayList<>();
public void addToMyList(Object element) { reallyAddToMyList(element); }
private void reallyAddToMyList(Object element) { myList.add(element); }
public static void main(String[] args) {
Application application = new Application();
application.myList.add("foo");
application.myList.add(new MyEntity());
application.addToMyList("bar");
application.addToMyList(new MyEntity());
}
}
Modified aspect:
package de.scrum_master.aspect;
import de.scrum_master.app.Entity;
public aspect EntityAddInterceptor {
pointcut addEntity(Object addedObject) :
cflow(execution(* *.addToMyList(*))) && (call(* *.add(*)) && args(addedObject));
before(Object addedObject) : addEntity(addedObject) {
if (addedObject.getClass().isAnnotationPresent(Entity.class))
System.out.println(thisJoinPointStaticPart + " -> " + addedObject);
}
}
New output:
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity#323ba00
As you can see, only one call is logged. It is the one from reallyAddToMyList(), not the one from main().
Update 2014-07-21 - better aspect modification:
Credits for this more elegant solution go to Andy Clement (AspectJ maintainer) who has mentioned it on the AspectJ mailing list. It shows both of my variants from above, but uses && #args(Entity) instead of if (addedObject.getClass().isAnnotationPresent(Entity.class)):
package de.scrum_master.aspect;
import de.scrum_master.app.Application;
import de.scrum_master.app.Entity;
public aspect EntityAddInterceptor {
pointcut addEntity(Object addedObject) :
within(Application) && call(* *.add(*)) && args(addedObject) && #args(Entity);
before(Object addedObject) : addEntity(addedObject) {
System.out.println(thisJoinPointStaticPart + " -> " + addedObject);
}
pointcut addEntitySpecial(Object addedObject) :
cflow(execution(* *.addToMyList(*))) && (call(* *.add(*)) && args(addedObject)) && #args(Entity);
before(Object addedObject) : addEntitySpecial(addedObject) {
System.out.println(thisJoinPointStaticPart + " -> " + addedObject + " [special]");
}
}
The output with both variants active looks like this:
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity#229ff6d1
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity#1976bf9e
call(boolean java.util.List.add(Object)) -> de.scrum_master.app.MyEntity#1976bf9e [special]
I would like some help on this matter,
Example:
public class A {
private void foo() {
//Who invoked me?
}
}
public class B extends A {}
public class C extends A {}
public class D {
C.foo();
}
This is basically the scenario. My question is how can method foo() know who is calling it?
EDIT: Basically I am trying to do a database Layer, and in class A I will create a method that will generate SQL statements. Such statements are dynamically generated by getting the values of all the public properties of the calling class.
Easiest way is the following:
String className = new Exception().getStackTrace()[1].getClassName();
But in real there should be no need for this, unless for some logging purposes, because this is a fairly expensive task. What is it, the problem for which you think that this is the solution? We may come up with -much- better suggestions.
Edit: you commented as follows:
basically i'am trying to do a database Layer, and in Class A i will create a method that will generate sql statements, such statements are dynamically generated by getting the values of all the public properties of the calling class.
I then highly recommend to look for an existing ORM library, such as Hibernate, iBatis or any JPA implementation to your taste.
Java 9: Stack Walking API
JEP 259 provides an efficient standard API for stack walking that allows easy filtering of, and lazy access to, the information in stack traces. First off, you should obtain an instance of StackWalker:
import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;
// other imports
StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
After that you can call the getCallerClass() method:
Class<?> callerClass = walker.getCallerClass();
Regardless of how you configured the StackWalker instance, the getCallerClass method will ignore the reflection frames, hidden frames and those are related to MethodHandles. Also, this method shouldn't be called on the first stack frame.
Perhaps for your use case it would make sense to pass the class of the caller into the method, like:
public class A { public void foo(Class<?> c) { ... } }
And call it something like this:
public class B { new A().foo(getClass() /* or: B.class */ ); }
foo() is private, so the caller will always be in class A.
if you using slf4j as your application logging system.
you can using:
Class<?> source = org.slf4j.helpers.Util.getCallingClass();
I think it's faster than new Exception().getStackTrace(), since getStackTrace() alaways doing clone stacktrace.
I would use StackWalker
private static Class<?> getCallingClass(int skip) {
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
Optional<? extends Class<?>> caller = walker.walk(frames ->
frames.skip(skip).findFirst().map(StackWalker.StackFrame::getDeclaringClass)
);
return caller.get();
}
If you need the class of the calling method use skip=1.
From a stack trace: http://www.javaworld.com/javaworld/javatips/jw-javatip124.html
A hacky solution is sun.reflect.Reflection.getCallerClass.
public void foo() {
Class<?> caller = sun.reflect.Reflection.getCallerClass();
// ...
}
It is hacky because you have to ensure that the class that calls Reflection.getCallerClass() is loaded on the bootstrap ClassLoader for the annotation #CallerSensitive (which getCallerClass is tagged with) to work. As such, it probably isn't the best solution for a project unless your project happens to use a Java Agent to add your classes to the bootstrap ClassLoader search.
With the following code, you obtain the first class which generated the stack of calls:
public String getInvonkingClassName(boolean fullClassNameNeeded){
StackTraceElement[] stack = new Exception().getStackTrace();
String className = stack[stack.length-1].getClassName();
if(!fullClassNameNeeded){
int idx = className.lastIndexOf('.');
className = className.substring(idx+1);
}
return className;
}
Boolean argument is used to get the full name including package name, or just class name.
StackFrame
The state of one method invocation on a thread's call stack. As a thread executes, stack frames are pushed and popped from its call stack as methods are invoked and then return. A StackFrame mirrors one such frame from a target VM at some point in its thread's execution.
JVM Stack: From Frame 1 get Frame 2 details
| |
| |
| Class2.function1() [FRAME 1] |
| executing the instructions |
|-------------------------------------------|
|Class1.method1() [FRAME 2] |
| called for execution Class2.function1() |
|-------------------------------------------|
Throwable::getStackTrace and Thread::getStackTrace return an array of StackTraceElement objects, which contain the class name and method name of each stack-trace element.
Throwable::getStackTrace contains the Stack with frames as Frame1(Top Frame) Current method, Frame2 calls Frame1 method for execution.
StackTraceElement[] stackTraceElements = (new Throwable()).getStackTrace();
// Frame1:Log4J.log(), Frame2:CallerClass
Thread::getStackTrace contains the stack with Frames:
Frame1:Thread.getStackTrace(), Frame2:Current Method, Frame3:Caller Method
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); //
sun.misc.SharedSecrets.getJavaLangAccess()
sun.misc.JavaLangAccess javaLangAccess = sun.misc.SharedSecrets.getJavaLangAccess();
StackTraceElement frame = javaLangAccess.getStackTraceElement((new Throwable()), callerFrame-1 ); // Frame0:Log4J.log(), Frame1:CallerClass
System.out.format("SUN - Clazz:%s, Method:%s, Line:%d\n", frame.getClassName(), frame.getMethodName(), frame.getLineNumber());
Throwable throwable = new Throwable();
int depth = javaLangAccess.getStackTraceDepth(new Throwable());
System.out.println("\tsun.misc.SharedSecrets : "+javaLangAccess.getClass() + " - StackTraceDepth : "+ depth);
for (int i = 0; i < depth; i++) {
StackTraceElement frame = javaLangAccess.getStackTraceElement(throwable, i);
System.out.format("Clazz:%s, Method:%s, Line:%d\n", frame.getClassName(), frame.getMethodName(), frame.getLineNumber());
}
JDK-internal sun.reflect.Reflection::getCallerClass method. It is deprecated, removed in Java9 JDK-8021946
Any way by using Reflection API we can't find the Line Number of Function which it get called.
System.out.println("Reflection - Called from Clazz : "+ Reflection.getCallerClass( callerFrame )); // Frame1:Log4J.log(), Frame2:CallerClass
Example:
static boolean log = false;
public static void log(String msg) {
int callerFrame = 2; // Frames [Log4J.log(), CallerClass.methodCall()]
StackTraceElement callerFrameStack = null;
StackTraceElement[] stackTraceElements = (new Throwable()).getStackTrace(); // Frame1:Log4J.log(), Frame2:CallerClass
//StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();// Frame1:Thread.getStackTrace(), Frame2:Log4J.log(), Frame3:CallerClass
int callerMethodFrameDepth = callerFrame; // Caller Class Frame = Throwable:2(callerFrame), Thread.currentThread:2(callerFrame+1)
for (int i = 0; i < stackTraceElements.length; i++) {
StackTraceElement threadFrame = stackTraceElements[i];
if (i+1 == callerMethodFrameDepth) {
callerFrameStack = threadFrame;
System.out.format("Called form Clazz:%s, Method:%s, Line:%d\n", threadFrame.getClassName(), threadFrame.getMethodName(), threadFrame.getLineNumber());
}
}
System.out.println(msg);
if (!log){
Logger logger = Logger.getLogger(callerFrameStack.getClass());
logger.info(msg);
}
}
public static void main(String[] args) {
Log4J.log("Log4J, main");
Clazz1.mc1();
Clazz21.mc12();
Clazz21.mc11();
Clazz21.mc21();
}
}
class Clazz1 {
public static void mc1() {
Log4J.log("Clazz1 - mc1");
}
}
class Clazz11 {
public static void mc11() {
Log4J.log("Clazz11 - mc11");
}
public static void mc12() {
Log4J.log("Clazz11 - mc12");
Clazz1.mc1();
}
}
class Clazz21 extends Clazz11 {
public static void mc21() {
Log4J.log("Clazz21 - mc21");
}
}
For Java 9 use Stack Walking API
I'm just answering this because for some reason the above answers started referring to exception handling - the original question had nothing to do with exceptions.
So, instead of trying to determine the caller of the method in question, and specifically to give more information dealing with the creation of a base class that generates SQL statements for its derived classes, here is an OO solution...
Make the base class abstract and include abstract methods that return the data it needs to build a sql statement.
This would include methods like...
getColumnList()
getFromTable()
getJoinedTables()
getFilterColumns()
The base class then does not care who is calling it because it is going to call up to the derived class for all the details it needs to create the SQL statement.
The base class knows the derived classes are going to provide the implementation of these methods because they are abstract.
Another way to implement this would be to have a SQLGenerator class that receives an interface with the methods described above and operates on the instances passed to it via those methods. For this, you would want to have the abstract methods described above moved into the interface, which all of your SQL related classes would implement.
List item
I tried this and it works well. It is because each Java Object has access to getClass() method which returns the class caller and the method name.
public Logger logger() {
return Logger.getLogger(getClass().toString());
}
example usage:
public DBTable(String tableName) {
this.tableName = tableName;
loadTableField();
this.logger().info("done");
}
sample output log using java.util.logging.Logger
Feb 01, 2017 11:14:50 PM rmg.data.model.DBTable (init) INFO: done
Maybe an answer is
public class CallerMain {
public void foo(){
System.out.println("CallerMain - foo");
System.out.println(this.getClass()); //output- callerMain
}
public static void main(String[] args) {
A a = new A();
CallerMain cm = new CallerMain();
cm.foo();
}
}
class A{
public void foo(){
System.out.println("A - foo");
System.out.println(this.getClass());//output- A
}
}