Santosh
Santosh

Reputation: 17903

Is there any way to know the caller class name?

Lets say I have a method classA.methodA() and its calling classB.methodB(). Now, inside the classB.methodB(), is there any way to know that its being called by classA (without passing any explicit info). I know this info is there in Java Runtime. My question is how to get the the class name of callee method ?

to make it more obvious

ClassA{

methodA(){
  ClassB b = new ClassB();
  b.methodB();

}

}


ClassB{

methodB(){
  // Code to find that its being called by ClassA
}

}

Upvotes: 4

Views: 7196

Answers (6)

Greg Bowyer
Greg Bowyer

Reputation: 1684

Warning (Potentially) Hotspot only and is not public API

Try sun.reflect.Reflection.getCallerClass(int numFramesToSkip); note that this API has been deprecated.

I last used this to do some evil ASM magics to re-write the log4j call on a third party library so that we could correct its dumb logging and get it to do log4j categories properly, no I will not mention what third party library as they are a company that is very touchy about reverse engineering.

Notes:

  • This is not the stackframe, its a reference to the class that made a call from that frame
  • This method will not give line numbers and other such things (the JVM has to "de-optimise" from jit code and reconstruct the debugging stanzas for that, one reason why stacktraces are slow)
  • This does not create a stacktrace
  • On hotspot the jit actively inlines this code making it super fast (look in globals.hpp for the switch and elsewhere in the code for the proof)

I can now hear the mob screaming about the none-public API example

Upvotes: 0

Polynomial
Polynomial

Reputation: 28316

You can use Thread.currentThread().getStackTrace() to get a stack trace, then iterate through it to find which method is above yours in the stack.

For example (totally made up stack here), you might have a stack like this:

main()
|- Foo()
   |- Bar()
      |- MyFunction()
         |- getStackTrace()
            |- captureJavaThreadStack(...)

Starting from the bottom, you iterate up until you hit the getStackTrace() method. You then know that the calling method is two methods up from that position, i.e. MyFunction() called getStackTrace(), which means that MyFunction() will always be above getStackTrace(), and whatever called MyFunction() will be above it.

You can use the getClassName() method on the StackTraceElement class to pull out the class name from the selected stack trace element.

Docs:

http://docs.oracle.com/javase/6/docs/api/java/lang/Thread.html#getStackTrace%28%29

http://docs.oracle.com/javase/6/docs/api/java/lang/StackTraceElement.html

Upvotes: 8

Aleksejs Mjaliks
Aleksejs Mjaliks

Reputation: 8707

Starting Java 5.0, you can use Thread.currentThread().getStackTrace() to get a current stack trace.

To get caller class (and method) of current method:

StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
int i = 1;
while (MyClass.class.getName().equals(stackTraceElements[i].getClassName())) { i++; }

int lineNumber = stackTraceElements[i].getLineNumber();
String className = stackTraceElements[i].getClassName();
String methodName = stackTraceElements[i].getMethodName();

Please note! There is some kind of warning in javadocs regarding this method:

Some virtual machines may, under some circumstances, omit one or more stack frames from the stack trace. In the extreme case, a virtual machine that has no stack trace information concerning this thread is permitted to return a zero-length array from this method.

I have not any trouble with this piece of code on Sun (Oracle) JVM and Mac OS X JVM. Please test your code carefully to be sure it works like you expect, especially if you use any other JVM.

Upvotes: 3

Only by inspecting the call stack which is not directly supported, but you can get an almost reliable answer by looking at either a stack trace (just create a RuntimeException) or a ThreadDump (for Java 6).

If you want to have your code behave differently based on who called it, you are most definitively heading in the wrong direction. Pass down the needed information explictly or use sub-classes to provide different behavior.

Upvotes: 3

GETah
GETah

Reputation: 21419

You can use the stack trace to do that. Inside the called method, do the following

Throwable t = new Throwable(); 
StackTraceElement[] stackTraceElements = t.getStackTrace(); 
String calleeMethod = stackTraceElements [0].getMethodName(); 
String callerMethodName = stackTraceElements [1].getMethodName(); 
String callerClassName = stackTraceElements [1].getClassName(); 
System.out.println("Caller :" + callerClassName + "." + callerMethodName); 

Upvotes: 2

Lukas Eder
Lukas Eder

Reputation: 220877

A not very reliable way is to look into

Thread.currentThread().getStackTrace()

It's not reliable, because there are various reasons why the callee is not what you'd expect, e.g. instrumentation, reflection utilities, etc. But maybe that's good enough for your use-case?

Upvotes: 1

Related Questions