5

Short version: I need to find a way in Java to locate the previous Method in the call stack (note the capital 'M' in Method--I need the actual java.lang.reflect.Method object).

Background: I'm working on an extension to the Google Guava Preconditions.checkNotNull(...) concept that will allow the programmer to simultaneously check ALL of the parameters for null, and construct an NPE based on the parameter names of the calling method. For example:

public class MyClass
{
    public void myMethod(Object arg1, Integer arg2, String arg3)
    {
        MyPreconditions.checkAllNotNull(arg1, arg2, arg3);
    }
}

So if the first argument was null, then the NPE might read arg1 cannot be null (MyClass:myMethod(Object, Integer, String)

I've already worked out how to get the calling Class and I can get the parameter names via the Spring LocalVariableTableParameterNameDiscoverer (or via the Paranamer library) one I have the Method. The only part that I can't figure out is how to get Method itself.

I'm aware that you can use the stack trace to get the method name, but that doesn't help if the method is overridden. Is there a way (even with internal com.sun classes) to get the actual stack? Alternatively, I can get the line number for the calling method, so is there a way to find the line number of a Method as retrieved from the Class object?

Cobra1117
  • 1,150
  • 3
  • 11
  • 26
  • Duplicate of http://stackoverflow.com/questions/421280/in-java-how-do-i-find-the-caller-of-a-method-using-stacktrace-or-reflection – james.garriss Nov 27 '13 at 12:04

2 Answers2

3

Thread.getStackTrace() gives you the current stacktrace. From there you can get the class name and method name (even if the method is overridden, you will see the exact class there), and get the method through reflection.

But imo - this is strange and not needed. And thing about it - you are providing a message that is mostly already present in the stacktrace that will be generated if the Preconditions call fails.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • You can't consistently resolve the `Method` object if the class comes from another classloader, however. – Dolda2000 Mar 26 '12 at 22:17
  • Well you can get the trace directly from the `Throwable`, something like: `try{throw new Exception("deliberate");} catch(Exception e){ e.getStackTrace(); }`. But agreed that it is somewhat odd to need it ... – user268396 Mar 26 '12 at 22:18
  • @Dolda2000 which is what the `getClassLoader()` method of `Class` objects is for. Either you get a valid `ClassLoader` instance or you get `null` which means the boot loader. Then you take it from there... ? – user268396 Mar 26 '12 at 22:20
  • @user268396 Sure, but the stacktrace doesn't give you `Class` objects, but just class names, and what I meant is that there is no consistent way to resolve `Class` objects from names, which would be the first necessary step to resolve `Method` objects from a stack trace. – Dolda2000 Mar 26 '12 at 22:21
  • @Dolda2000 True, you would need to do it from inside a Class for which the classloader has access to the actual Class in some way --or at least to delegate classloader's that will co-operate nicely. – user268396 Mar 26 '12 at 22:28
  • @Dolda2000 I came up with two easy ways to resolve the calling `Class`. First, you can create your own security manager and call the protected `getClassContext()` method. (Your class doesn't have to be the active security manager to do this.) The second option is EVIL, but fast--you can call `sun.reflect.Reflection.getCallerClass(...);` – Cobra1117 Mar 26 '12 at 23:18
  • @Bozho This does give you the method name, but you can't retrieve the parameter names from the method name. (And if the method is overloaded, trying to retrieve the `Method` from the `Class` via method name matching won't work.) Because of some of our documentation/testing requirements, I need to have the parameter name in the NPE's message. (And as I mentioned, I can can get the parameter name, if I have the `Method`) – Cobra1117 Mar 26 '12 at 23:20
  • @Cobra'sCreations You can't actually create a new SecurityManager in case one is active, however (unless you have been given permission to, of course). See the description of its constructor; it throws a SecurityPermission if a security manager already exists. – Dolda2000 Mar 27 '12 at 04:58
  • @Dolda2000 Good point. It's not a concern in our case because we control the execution environment, but good to keep in mind anyway! :-) – Cobra1117 Mar 28 '12 at 03:30
1

"I'm aware that you can use the stack trace to get the method name, but that doesn't help if the method is overridden." -- The stack trace contains the class name (and source file) as well, though, in a manner that is correct even if the method has been overridden.

I don't think there is any (good, consistent) way to get the actual Method object. Particularly not one that works with an active security manager. The Java security code itself uses some tricks to do that, but replicating that would not be portable between various VMs.

Dolda2000
  • 25,216
  • 4
  • 51
  • 92