Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I get a reference to a calling class in a static method?

Tags:

java

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

like image 531
yegor256 Avatar asked Nov 11 '10 07:11

yegor256


2 Answers

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.

like image 154
T.J. Crowder Avatar answered Oct 05 '22 02:10

T.J. Crowder


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.

like image 31
Chris Moran Avatar answered Oct 05 '22 02:10

Chris Moran