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.
- Ignore native methods.
- Ignore OpenJDK internal classes involved with reflective invocation
- 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.