Imagine you’re going to find the caller of the current executing method. To make it even more interesting, imagine you’re going to get current thread’s stack frames one by one and filter them to extract or expose some meaningful information. How would you do these?
Before Java 9, there were three different approaches that we could use to solve mentioned problems:
Thread::getStackTracewhich return an array of
StackTraceElementrepresents a stack frame. Both approaches would force the VM to eagerly capture a snapshot of the entire stack, which is very inefficient if you are only interested in the top few frames on the stack. Also,
StackTraceElementdoesn’t retain class information and we’re just have access to the class name. Obviously,
Class<?>tokens are far more desirable in some use cases.
protectedmethod, which allows a
SecurityManagersubclass to access the class context. Sure it’s working and somewhat efficient but if we only had a more pleasant API at our disposal!
Last but not certainly least, there is my beloved, deprecated and JDK internal
sun.reflect.Reflection::getCallerClasswhich is a very efficient way to find the caller of current executing method. Regardless of the fact that it’s now deprecated, it’s also an internal API which we shouldn’t use in the first place.
Just to recap, current solutions are inefficient, incomplete, inconvenient and forbidden! JEP 259 is an attempt to address these issues.
Stack Walking API
JEP 259 or the Stack Walking API provides a standard and efficient API for stack walking (surprise!) that allows easy filtering of, and lazy access to, the information in stack traces.
StackWalker class is the main entry point to access the Stack Walking API. Hence, In order to work with it, we should first obtain an instance of
StackWalker using one of four different flavors of
getInstance factory method. Those different overloaded versions of
getInstance differ in how much information you want to obtain and how deep you’re willing to go in the stack!
So who’s calling then?
Let’s write some code! Suppose we’re going to find the caller of the current executing method:
If we run the preceeding code, we’d get the following exception:
Exception in thread "main" java.lang.UnsupportedOperationException: This stack walker does not have RETAIN_CLASS_REFERENCE access
The exception is very informative. When we call the
StackWalker.getInstance() factory method, we get a
StackWalker with default configuration which would not retain class information. In order to fix this, we can configure the
StackWalker.Option.RETAIN_CLASS_REFERENCE option which instructs the
StackWalker to retain
Class information in each
Running this modified version would print the following on console (Assuming we’re calling
whoIsIt from a method in class
Let’s go for a walk
StackWalker provides a
walk method which enables us to traverse the stack frames without performing an eager capture of whole stack. The walk API takes a function that map the given
Stream<StackFrame> to anything we want. For example, in order to find the caller of current executing method:
walk method opens a sequential stream of
StackFrames for the current thread and then applies the function with the
StackFrames stream. Also, when the
walk method returns, that
Stream will be automatically closed.