Some interesting things to note: 1) Someone has been asking for a public API replacement for getCallerClass() since Java 4 (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4851444). I /would/ have been asking for this for that long, except I haven't needed it until recently and assumed it already existed. .NET has had an API similar to what I proposed below since .NET 1.1.
2) http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8014925 discusses the change to getCallerClass(int) and says it will be completely removed (with no system property to re-enable) as early as 7u55. The bug then talks about how feedback needs to be solicited on how the community is using it so that a public API, if needed, can be created. This is that feedback, and the answer appears to be a resounding YES, a public API needs to replace it because this change is disastrous for many projects. As Paul says, at the very least getCallerClass(int) should keep working like it does in <7u25 (with or without a system property) through the rest of Java 7 and Java 8 if a replacement public API is not adopted. 3) The performance test numbers below don't reflect the fact that some use cases (like Log4j) have to use Thread/Throwable#getStackTrace() AND Reflection/SecurityManager. These use cases need the method name/line number AND the Class instance. If Reflection#getCallerClass() is taken away from us and SecurityManager#getClassContext() is unavailable (possible if an existing SecurityManager prohibits instantiating the SecurityManager class), these use cases have to call getStackTrace() and THEN Class#forName() for each StackTraceElement. I'm sure you can see where performance becomes a serious issue at that point. The proposed API below was designed to solve all of the uses cases that have been discussed in a well-performing way. Nick On Jul 29, 2013, at 10:47 AM, Jörn Huxhorn wrote: > The numbers are from this link: > http://stackoverflow.com/questions/421280/in-java-how-do-i-find-the-caller-of-a-method-using-stacktrace-or-reflection > > Even if this benchmark suffers from micro-benchmark issues: > a slow-down of 10x would be bad, a slow-down of 100x would be a catastrophe. > > I'd suggest to at least postpone the UnsupportedOperationException change > until we find a suitable replacement. This change will also break existing > Groovy scripts. See http://jira.codehaus.org/browse/GROOVY-6279 - but there > are other issues as well. > > Cheers, > Jörn. > > On 29. Juli 2013 at 16:49:02, Nicholas Williams > (nicholas+open...@nicholaswilliams.net) wrote: > > I wasn't the one who ran the test, so I don't know for sure. My theory > was that getCallerClass() returns a single frame, but the > SecurityManager must allocate an array of appropriate size (which > involves some overhead) and then return all of the frames. I chalked > the difference up to that. My conclusion from the data was: If you > need a whole stack, SecurityManager is clearly the best option. If you > need a single frame, getCallerClass() is the only option that makes > any sense. > > On Mon, Jul 29, 2013 at 8:21 AM, David M. Lloyd <david.ll...@redhat.com> > wrote: >> I find it very interesting that reflection is no less than two orders of >> magnitude faster than the security manager solution. How big was the stack >> in these tests? It makes me wonder if maybe the implementation of the >> security manager's getContext() method should be reevaluated a bit. >> >> >> On 07/29/2013 07:53 AM, Nick Williams wrote: >>> >>> 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 >>>> >>> >>> >> >> >> -- >> - DML