8

I have a static Java method, where I want to know who is its caller. Is it possible to get this information in Java?

asked Nov 11, 2010 at 7:49
1
  • search around, there are multiple questions. The key is to get the stack trace, but it is not advisable to do so Commented Nov 11, 2010 at 7:52

4 Answers 4

15

It's possible, but it's expensive and in anything remotely like a normal case, it's a very bad idea. You almost certainly want to approach the problem you're solving in another way. (And if you post a new question about the problem you're solving, I bet someone will help you do that! :-) )

Two ways you can generate a stack trace:

1) Via Throwable

...by throwing and catching an exception, and then using Exception#getStackTrace

try {
 throw new Exception();
}
catch (Exception e) {
 // Get the stack trace
 StackTraceElement[] entries = e.getStackTrace();
}

...or as lscoughlin points out (please vote him/her up), rather more directly through simply new Throwable:

StackTraceElement[] entries = new Throwable().getStackTrace();

Each of those StackTraceElements then gives you information about that point in the stack trace. In your case, you'd probably want to look at StackTraceElement's getClassName method. If you really need a reference to the calling class object, you can pass that class name string into Class.forName but be warned that in complex environments, you might get a different instance (or no instance) of the class if the caller has done interesting things with class loaders.

2) By using Thread#getStackTrace

MRalwasser helpfully points out below that in Java 5 or higher (and I'm guessing you're probably using Java 5 or higher), you can use Thread.currentThread().getStackTrace() instead of actually throwing an exception. I don't know whether it would be lighterweight or not (as getting the stack trace may well be what's expensive about throwing an exception), but it's definitely cleaner.

In a quick and dirty test, counter-intuitively actually throwing the exception seemed faster than going via Thread (and was pretty much the same as new Throwable), but the vagaries of naive profiling of JVM applications are well documented, your mileage may vary...

But again, not only is getting a stack trace an expensive process, but using information about the caller that the caller hasn't passed you via the method signature is a serious design issue in anything other than (say) a debugger or error tracer used only for development. The main answer here has to be: Don't do that, unless you have a really good reason to.

answered Nov 11, 2010 at 7:52
Sign up to request clarification or add additional context in comments.

7 Comments

+1 for the bad idea. Though it might be temporarily useful for debugging purposes.
@mklhmmm: Exactly, I've totally used it for debugging when the situation was hard to walk through with a debugger.
If Java 5 or above is being used, you should use Thread.getCurrentThread().getStackTrace() instead. Although it's the same idea, it should be little bit more lightweight.
@TJ I think that you should edit your answer and place Thread.getCurrentThread().getStackTrace() on top of it.
@Vincenzo: I think I'll leave it, but I'll make it clearer. In the meantime, I've corrected it -- it's Thread.currentThread rather than Thread.getCurrentThread. Ah, the joys of an inconsistent API... ;-)
|
8

Sorry to resurrect this thread but the mechanism I've been using for ages to accomplish this same thing without the cumbersome use of stacktraces.


class ClassloaderUtil {
 public static Class getCallingClass() {
 return CallerResolver.getCallerClass(2);
 }
 private static final class CallerResolver extends SecurityManager {
 private static final CallerResolver CALLER_RESOLVER = new CallerResolver();
 private static final int CALL_CONTEXT_OFFSET = 3; // may need to change if this class is redesigned
 protected Class[] getClassContext() {
 return super.getClassContext();
 }
 /*
 * Indexes into the current method call context with a given
 * offset.
 */
 private static Class getCallerClass(int callerOffset) {
 return CALLER_RESOLVER.getClassContext()[CALL_CONTEXT_OFFSET + callerOffset];
 }
 private static int getContextSize() {
 return CALLER_RESOLVER.getClassContext().length - CALL_CONTEXT_OFFSET;
 }
 }
}

Then using it is as simple as this:

public class ClassloaderUtilTest {
 @Test
 public void test() {
 Class c = ClassloaderUtil.getCallingClass();
 Assert.assertNotNull(c);
 c = foo();
 Assert.assertNotNull(c);
 }
 private Class foo() {
 return ClassloaderUtil.getCallingClass();
 }
}

The first class will be some junit framework class whereas foo() will return ClassloaderUtilTest as the class.

It is definitely not perfect. However, it does have its random uses. I agree with the folks that have already answered this question in that this is incredibly expensive.

answered Jul 13, 2012 at 11:55

1 Comment

How expensive is this?
7

It's easier and slightly safer to just new a thowable, as in:

 Throwable t = new Throwable();
 StackTraceElement directCaller = t.getStackTrace()[1];

But Generally speaking -- it's still a terrible idea, and it is expensive.

UPDATE: Since java9 you you have the StackWalker - so I use a thing that looks like this now.

import java.lang.StackWalker.StackFrame;
public class Backtrace {
 private static final StackWalker stackWalker = StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES);
 public static StackFrame backTrace() {
 var stackFrame = stackWalker.walk((stream -> stream
 .limit(2)
 .reduce((one, two) -> two)));
 return stackFrame.orElseThrow();
 }
 public static StackFrame backTrace(int framesBack) {
 var stackFrame = stackWalker.walk((stream -> stream
 .limit(2 + framesBack)
 .reduce((one, two) -> two)));
 return stackFrame.orElseThrow();
 }
}
answered Nov 11, 2010 at 9:49

2 Comments

new Throwable Doh!! And to make things worse, this is not even the first time I've made that mistake... Big +1.
Ha! @T.J.Crowder Thanks for the kudos :P
1

Check out this solution using ASM for a similar previous question. Unfortunately my own answer was not quite that good. But I agree with the bad idea part.

answered Nov 11, 2010 at 8:20

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.