Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

The Stack Walking API in Java 9

DZone's Guide to

The Stack Walking API in Java 9

Java 9's Stack Walking API solves a number of problems when attempting to obtain meaningful information from your stack frames. Let's see it in action.

· Java Zone ·
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

Status Quo

Imagine you're going to find the caller of the currently 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 that?

Before Java 9, there were three different approaches that we could use to solve those mentioned problems:

  • Throwable::getStackTrace and Thread::getStackTrace, which return an array of StackTraceElements. Each StackTraceElement represents 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, StackTraceElement doesn't retain class information, and we just have access to the class name. Obviously, Class<?> tokens are far more desirable in some use cases.
  • SecurityManager::getClassContext is a protected method, which allows a SecurityManager subclass to access the class context. Sure, it works and is somewhat efficient, but if we only had a more pleasant API at our disposal!
  • Last, but not certainly least, there is my beloved, deprecated JDK internal sun.reflect.Reflection::getCallerClass, which is a very efficient way to find the caller of the currently 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, the 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.

The 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 the 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 currently executing method:

public class Foo {
    public static void whoIsIt() {
        StackWalker walker = StackWalker.getInstance();
        System.out.println(walker.getCallerClass());
    }
}


If we run the preceding 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 a default configuration that would not retain class information. In order to fix this, we can configure the StackWalker with the StackWalker.Option.RETAIN_CLASS_REFERENCE option, which instructs the StackWalker to retain Class information in each StackFrame:

public class Foo {
    public static void whoIsIt() {
        StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
        System.out.println(walker.getCallerClass());
    }
}


Running this modified version would print the following on the console (assuming we're calling whoIsIt from a method in the class me.alidg.Bar):

class me.alidg.Bar


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 the whole stack. The walk API takes a function that maps the given Stream<StackFrame> to anything we want. For example, in order to find the caller of the currently executing method:

Optional<? extends Class<?>> caller = walker.walk(frames -> 
                frames.skip(1).findFirst().map(StackFrame::getDeclaringClass)
        );


The 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.

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
java ,java 9 ,stack walking api ,stack trace ,tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}