Determining caller class using StackTrace Elements

0

In this article, we will see how to determine the caller class using Stack Trace elements.  We will iterate through the StackTraceElement array returned byThrowable#getStackTrace() and then determine from it the class of the calling code. The class name figured out will be converted to a class usingClass.forName(className).

In our previous article, we have seen how to determine the caller class using sun.reflect.Reflection. Ideally, we shouldn’t be writing programs that call classes from sun.* packages.

A Java program that directly calls into sun.* packages is not guaranteed to work on all Java-compatible platforms.

The classes in sun.* are present in the JDK to support Oracle’s implementation of the Java platform: the sun.* classes are what make the Java platform classes work “under the covers” for Oracle’s JDK. These classes will not in general be present on another vendor’s Java platform. More details here.

ClassContext:

package com.javarticles.reflection;

public class ClassContext {
    private Class<?> clazz;
    
    public ClassContext() {  
        clazz = StackTraceUtil.getCallerClass(2);
    }
    
    public ClassContext(Class<?> clazz) {
        this.clazz = clazz;
    }

    public Class<?> getCallerClass() {
        return clazz;
    }        
}

We want to ignore those stack trace elements which contain the state of the below special java methods.

  1. Ignore native methods.
  2. Ignore OpenJDK internal classes involved with reflective invocation
  3. Ignore use of reflection including Mehod.invoke(), InvocationHandler.invoke(), Constructor.newInstance(), Class.newInstance().

StackTraceUtil:

package com.javarticles.reflection;


public class StackTraceUtil {

    public static Class<?> getCallerClass(final int index) {
        final StackTraceElement[] elements = new Throwable().getStackTrace();
        int i = 0;
        for (final StackTraceElement element : elements) {
            System.out.println("Class at index [" + i + "]->" + element.getClassName());
            ++i;
        }
        if (index < 0) {
            throw new IndexOutOfBoundsException(Integer.toString(index));
        }
        try {
            return getCallerClassFromStackTrace(index + 1);
        } catch (final ClassNotFoundException e) {
            System.out
                    .println("Could not find class in ReflectionUtil.getCallerClass({}), index<" + index + ">" + ", exception< " + e + ">");
        }
        return null;
    }

    private static Class<?> getCallerClassFromStackTrace(final int index)
            throws ClassNotFoundException {

        final StackTraceElement[] elements = new Throwable().getStackTrace();
        int i = 0;
        for (final StackTraceElement element : elements) {
            if (isValidlMethod(element)) {
                if (i == index) {
                    return Class.forName(element.getClassName());
                }
                ++i;
            }            
        }
        System.out
                .println("Could not find an appropriate StackTraceElement at index "
                        + index);
        throw new IndexOutOfBoundsException(Integer.toString(index));
    }

    private static boolean isValidlMethod(final StackTraceElement element) {
        if (element.isNativeMethod()) {
            return false;
        }
        final String cn = element.getClassName();
        if (cn.startsWith("sun.reflect.")) {
            return false;
        }
        final String mn = element.getMethodName();
        if (cn.startsWith("java.lang.reflect.")
                && (mn.equals("invoke") || mn.equals("newInstance"))) {
            return false;
        }
        if (cn.equals("java.lang.Class") && mn.equals("newInstance")) {
            return false;
        }
        return true;
    }

}

Let’s now test it out. We will create ClassContext instance and then verify whether the caller class name is found. In our second case, we will create instance of ClassContext using reflection.

StackTraceCallerClassExample:

package com.javarticles.reflection;

public class StackTraceCallerClassExample {
    public static void main(String[] args) throws Exception {
        ClassContext classContext = new ClassContext();
        System.out.println(classContext.getCallerClass().getName());

        Class<?> classContextClass = Class
                .forName("com.javarticles.reflection.ClassContext");
        Object classContextInstance = classContextClass.newInstance();
        System.out.println(((ClassContext) classContextInstance)
                .getCallerClass().getName());
    }
}

You can see the order of stack trace elements. There are elements for sun.reflect.* and java.lang.* which are ignored.

Output:

Class at index [0]->com.javarticles.reflection.StackTraceUtil
Class at index [1]->com.javarticles.reflection.ClassContext
Class at index [2]->com.javarticles.reflection.StackTraceCallerClassExample
com.javarticles.reflection.StackTraceCallerClassExample
Class at index [0]->com.javarticles.reflection.StackTraceUtil
Class at index [1]->com.javarticles.reflection.ClassContext
Class at index [2]->sun.reflect.NativeConstructorAccessorImpl
Class at index [3]->sun.reflect.NativeConstructorAccessorImpl
Class at index [4]->sun.reflect.DelegatingConstructorAccessorImpl
Class at index [5]->java.lang.reflect.Constructor
Class at index [6]->java.lang.Class
Class at index [7]->com.javarticles.reflection.StackTraceCallerClassExample
com.javarticles.reflection.StackTraceCallerClassExample

Download the source code

This was an example about determining caller class using Stack Trace elements.

You can download the source code here: javaCallerClassFromStacktraceExample.zip
Share.

Comments are closed.