Find calling Method in Java - java

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?

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.

"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.

Related

Prohibiting passing certain values for method parameters

I have a method like this:
void method(int number){some code}
and i will call it like this:
method(-1)
is there a way in java to only allow passing positive integers to number parameter other than checking in the method body or making a checked exception?
Declaring a checked exception doesn't make method parameters automatically validated somehow, it just means that the caller is obligated to check whether it was thrown, even if the code calls a literal method(1).
If your application is complex enough, you can use Bean Validation and put a constraint on the method parameter:
void method(#Min(1) int number) { }
This is only worthwhile if you're already using a system complex enough to provide support for it, such as Spring or CDI. Otherwise, just stick to checking in the method body and throwing IllegalArgumentException if the requirements fail. Guava's Preconditions utility can be helpful here.
(Additionally, your code will be much easier to read if you follow the universal Java code standards. Type names start with capitals, but member and parameters names start with lowercase.)

Getting annotation value of calling method

I want to get a value in annotation from method that is calling another method and that needs to be used with annotation used in called method.
Example
#MyAnnotation(id="method-invoker")
public void invoker(){
executor();
}
#ChildMethod
public void executor(){
}
In above example I want to get value set in id field in #MyAnnotation when handling #ChildMethod annotation.
How can I do this?
First you have to get the stack trace, then extract caller's name from it, then get the Method and get its annotation:
String id = getClass().getMethod(new Throwable().getStackTrace()[1].getMethodName()).getAnnotation(MyAnnotation.class).id();
(obviously it is a bad style to perform so many calls sequentially in one line, but it is ok for example).
But this method is limited. It does not support methods overloading. For example if you have 2 methods called invoker() that have different signature in one class you cannot distinguish between them using pure reflection API.
Fortunately I implemented library named CallStack that can do this: https://github.com/alexradzin/callstack
Using CallStack you can say:
CallStack.getCallStack().getFunction().getAnnotation(MyAnnotation.class).id()

Is it possible to convert method reference to MethodHandle?

Is it possible to convert a method reference (e.g. SomeClass::someMethod) to a MethodHandle instance? I want the benefits of compile-time checking (ensuring that the class and method exists) as well as the ability to introspect the method using the MethodHandle API.
Use-case: I've got code that needs to execute if and only if the request was not triggered by a specific method (to avoid endless recursion). I want a compile-time check to ensure the class/method exists but a runtime check to compare the caller to the method.
So to recap: Is it possible to convert a method reference to a MethodHandle?
Well, if you can afford the additional overhead and security implications, you can use a Serializable functional interface and decode the serialized form of the method reference instance to find the target like demonstrated in this answer or brought up again with this question and its answers.
However, you should really rethink your software design. “Avoiding endless recursion” shouldn’t be fixed by decoding some kind of parameter object, especially not if your assumption is, that this actual argument value represents the caller of your method. How would you ever enforce this strange relationship?
Even a simple code change like referencing a method which delegates to the other method would break your check. Here is a simple example showing the subtle problems with your approach:
public class SimpleTest {
public static void main(String... arg) {
run(SimpleTest::process);
}
static void run(BiConsumer<Object,Object> c) {
c.accept("foo", "bar");
}
static void process(Object... arg) {
Thread.dumpStack();
}
}
When running this program it will print something like:
java.lang.Exception: Stack trace
at java.lang.Thread.dumpStack(Thread.java:1329)
at SimpleTest.process(SimpleTest.java:16)
at SimpleTest.lambda$MR$main$process$a9318f35$1(SimpleTest.java:10)
at SimpleTest$$Lambda$1/26852690.accept(Unknown Source)
at SimpleTest.run(SimpleTest.java:13)
at SimpleTest.main(SimpleTest.java:10)
showing that the method reference within the generated instance is not the expected SimpleTest::process but instead SimpleTest::lambda$MR$main$process$a9318f35$1 which will eventually invoke process. The reason is that some operations (here varargs processing) are not performed by the generated interface instance but a synthetic method instead, just like you had written run((a,b)-> SimpleTest.process(a,b)). The only difference is the name of the synthetic method.
You shouldn’t design software relying on such fragile introspection. If you want to avoid recursion, a simple ThreadLocal flag telling whether you are already inside your specific method would do the job. But it might be worth asking yourself why your API is provoking endless recursion in the first place; there seems to be something fundamentally wrong…

Get parameter values of the calling method [duplicate]

This question already has answers here:
How to get arguments passed to method that called this method?
(6 answers)
Closed 3 years ago.
How to get parameter values of the calling method?
I have scenario where there are two classes viz., Class A and Class B containing two methods mthA(in Class A) and mthB(in Class B). mthA calls mthB. now I want to know in mthB that what all parameters have been passed to mthA. This is basically for logging and handling exceptions. I am able to get the class name of the calling class i.e Class A and the method name which is calling mthB i.e mthA. but I am stuck at getting the parameters of mthA.
Thread.currentThread().getStackTrace()[1].getClassName()
this gives me the class name of the calling method i.e Class A.
Thread.currentThread().getStackTrace()[2].getMethodName()
This gives me the method name of the calling method i.e mthA.
If somehow I can get the whole method itself then I can get the method parameters passed to this method by using getParameters().
There's no easy way to get calling method's arguments values, (if you can not change the parameters of called method to include and pass calling parameters values).
Using some APIs like what Thread provides, we can get the static info like class/method name, but not runtime info like arguments values. One way could be to store and manage that kind of info somewhere and show when required in case of logging/exception. I dont think there's an API for that in java.
There is simplest way or is it necessary to use Reflection..?
If you want to know what parameter is passed for mthA then pass same parameter to mthB also Ex:
mthA(1stParam, 2ndParam, ...){
mthB(1stParam, 2ndParam, ..., OtherParamertsForMthB);
...
}
If it is just for logging purpose only, then you should log in mthA itself all the arguments passed into the method.
Why should mthB know about mthA's argument values to handle exceptions? If you are doing that, then you should consider refactoring the code and handle exceptions at the appropriate levels. If values passed to mthA is leading to an exception, then it should be handled there itself before calling mthB.
Reflection only helps in getting static information of the class and methods and does not provide runtime values of the arguments passed into a method.
Why don't you use some AOP tool like AspectJ to capture these values and log? You can use execution() point cut along with after() advice. For non production deployment you can log all the method calls along with passed values and returned value. This will be too much overhead for production env. For that you can just store the passed values (Object args[] as you get in AspectJ advice) in local variable and only log it in case of exception. But even in that case there will be some performance penalty as primitive values will be boxed to be passed as Object[] to your advice.

How can a Java method retrieve the method object pertaining to that particular method? (Java)

I am writing a Java method with the following signature.
void Logger(Method method, Object[] args);
If a method (e.g. ABC() ) calls this method Logger, it should retrieve the Method object that encapsulates data about itself (ABC()) and pass it as an argument.
How can a method retrieve the Method object that is storing all the information about that method?
A simple way is that I use
Method[] methods = ExampleClass.Class.getMethods();
and search the whole array for the Method with the correct name. (Which is quite inefficient). Also, if two or more methods have the same names, then I will have to retrieve their parameter types too (to distinguish them) and have different code for each method. This would be inefficient as well as painful.
Is there a better way?
Thanks for the help.
Don't do it. Rather obtain the method name from the stack.
public void log(Object object) {
String methodName = Thread.currentThread().getStackTrace()[2].getMethodName();
// ...
}
This is however pretty expensive and that's why most self-respected logging frameworks offer an option to turn it on/off (which I would recommend to use instead of homegrowing one).
Even better, don't implement this method at all. Use logback, or some other modern logging framework.
If you are writing a wrapper over existing logger frameworks then the already provide a way to print the method name in the log message - if that's what you are trying to implement.
You can read from the log4j documentation, for example, that this extraction (as the other answer suggests) is done from the stack trace and is expensive : http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html

Categories

Resources