Just so that everyone understands how important this subject is, this change to 
getCallerClass(...) is being labeled a "disaster" for logging frameworks 
everywhere. Here's a benchmark for getting Classes from the following methods:

> 1,000,000 calls of all alternatives were measured as follows :
> Reflection: 10.195 ms.
> Current Thread StackTrace: 5886.964 ms.
> Throwable StackTrace: 4700.073 ms.
> SecurityManager: 1046.804 ms.

My goal here is to get the entire list engaged in coming up with the right 
solution. We (the community) can't afford for Java 8 not to have an equivalent 
replacement for getCallerClass().

Nick

On Jul 27, 2013, at 2:01 PM, Nick Williams wrote:

> All,
> 
> In the last two months, there have been a number of discussions surrounding 
> stack traces, Classes on the stack trace, and caller classes [1], [2], [3]. 
> These are all related discussions and the solution to them is equally 
> related, so I wanted to consolidate it all into this one discussion where I 
> hope we can finalize on a solution and get it implemented for Java 8.
> 
> In a nut shell, here are the underlying needs that I have seen expressed 
> through many, many messages:
> 
> - Some code needs to get the Class of the caller of the current method, 
> skipping any reflection methods.
> - Some code needs to get the Class of the caller /n/ stack frames before the 
> current method, skipping any reflection methods.
> - Some code needs to get the current stack trace, populated with Classes, 
> Executables, file names, line numbers, and native flags instead of the String 
> class names and String method names in StackTraceElement. This /should/ 
> include any reflection methods, just like StackTraceElement[]s.
> - Some code needs to get the stack trace from when a Throwable was created, 
> populated with Classes, Executables, file names, line numbers, and native 
> flags instead of the String class names and String method names in 
> StackTraceElement. This /should/ include any reflection methods, just like 
> StackTraceElement[]s.
> - There needs to be a reflection way to achieve all of this since some 
> libraries (e.g., Log4j) need to be compiled against Java 6 but run on 7 and 8 
> (and thus can't use @CallerSensitive).
> 
> I believe the solutions to these needs are all related. Importantly, I think 
> it is very important that action be taken in Java 8 due to the changes made 
> to sun.reflect.Reflection#getCallerClass(...). While we all understand that 
> relying on private sun.* APIs is not safe, the fact is that many people have 
> relied on sun.reflect.Reflection#getCallerClass(...) due to the fact that 
> there is simply no other way to do this in the standard API. This includes 
> Log4j 2, Logback, SLF4j, and Groovy, some features of which will stop working 
> correctly in Java 7 >= u25.
> 
> I would point out that this could all easily be solved simply by adding a 
> getElementClass() method to StackTraceElement, but there was strong 
> opposition to this, largely due to serialization issues. Since that is 
> apparently not an option, I propose the following API, based on the various 
> discussions in the last two months, StackTraceElement, and the API that .NET 
> provides to achieve the same needs as listed above:
> 
> CallerSensitive.java:
> package java.lang;
> 
> /** Previously private API, now public */
> public @interface CallerSensitive {
>     ...
> }
> 
> StackTraceFrame.java:
> package java.lang;
> 
> import java.util.Objects.
> 
> public final class StackTraceFrame {
>     private final Class<?> declaringClass;
>     private final Executable executable;
>     private final String fileName;
>     private final int lineNumber;
> 
>     public StackTraceFrame(Class<?> declaringClass, Executable executable, 
> String fileName, int lineNumber) {
>         this.declaringClass = Objects.requireNonNull(declaringClass, 
> "Declaring class is null");
>         this.executable = Objects.requireNonNull(executable, "Executable is 
> null");
>         this.fileName = fileName;
>         this.lineNumber = lineNumber;
>     }
> 
>     public Class<?> getDeclaringClass() {
>         return this.declaringClass;
>     }
> 
>     public Executable getExecutable() {
>         return this.executable;
>     }
> 
>     public String getFileName() {
>         return this.fileName;
>     }
> 
>     public int getLineNumber() {
>         return this.lineNumber;
>     }
> 
>     public boolean isNative() {
>         return this.lineNumber == -2;
>     }
> 
>     public String toString() { /* Same as StackTraceElement */ }
>     public boolean equals() { /* Ditto */ }
>     public int hashCode() { /* Ditto */ }
> 
>     /** Uses @CallerSensitive */
>     public static native StackTraceFrame getCallerFrame();
> 
>     /** Works like Java < 7u25 sun.reflect.Reflection#getCallerClass() */
>     public static native StackTraceFrame getCallerFrame(int skipFrames);
> 
>     public static native StackTraceFrame[] getCurrentStackTrace();
> }
> 
> Throwable.java:
> package java.lang;
> 
> ...
> 
> public class Throwable {
>     ...
>     public synchronized Throwable fillInStackTraceFrames() { ... }
> 
>     private native Throwable fillInStackTraceFrames(int dummy);
> 
>     public StackTraceFrame[] getStackTraceFrames() {
>         return this.getOurStackTraceFrames().clone();
>     }
> 
>     private synchronized StackTraceFrame[] getOurStackTraceFrames() { ... }
>     ...
> }
> 
> Furthermore, I propose that we restore the behavior of 
> sun.reflect.Reflection#getCallerClass(int) /just for Java 7/ since the 
> proposed above solution cannot be added to Java 7.
> 
> I would love if we could quickly coalesce around this solution or a 
> derivative thereof so that it can be implemented before Feature Complete. The 
> absence of any replacement or alternative for 
> sun.reflect.Reflection#getCallerClass(int) will be a serious issue in Java 8 
> that will cause hardships for many projects.
> 
> [1] http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-June/018049.html
> [2] 
> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-June/018349.html, 
> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-July/019098.html
> [3] http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-July/018855.html

Reply via email to