Issue
I'm looking for a way to output into file all methods, that are calling all methods in a specified class. I see that I could use ASM for this, but I cannot figure it out.
Example: class A has methods aA, bA, cA. class B calls methods aB -> aA, bB -> bA, cB -> cA.
I want to output "aB -> aA, bB -> bA, cB -> cA" into file.
My main goal is to trace all these calls from ExceptionFactory to service layer, and then output which service methods can throw specific exceptions.
The most suitable way for me would be, that it would run as a test, the file would be created during test phase.
EDIT: I actually don't know what class B is, I need to find out which class/classes calls class A.
Solution
You can achieve something like above in your question by making use of JavaAssist
library as given here : Java Assist - Java bytecode engineering toolkit
This Jar file provides you with methods that can help you inspect your classes and its methods much more deeply to fetch that kind of information (i.e , methods in a given class are invoking method from which particular class) . For example , lets take example of this class A
as below :
public class A {
void meth1()
{
System.out.println("meth1 A");
}
void meth2()
{
System.out.println("meth2 A");
}
void meth3()
{
System.out.println("meth3 A");
}
}
Now , let's take another class B
, which has also 3 methods . Inside each of these 3 methods , methods of A are being called as below :
public class B {
A a =new A();
void meth1()
{
System.out.println("meth1 B");
a.meth1();
}
void meth2()
{
System.out.println("meth2 B");
a.meth2();
}
void meth3()
{
System.out.println("meth3 B");
a.meth3();
}
}
As you can see , methods in class B
are invoking methods of class A
from inside them . Now, we can make use of methods of JavaAssist library as shown below , to fetch the information that from each method of class B
, which method of A
has been called ? The method.instrument
construct from the JavaAssist library allows you to build a bytecode manipulation expression , which helps you to list methods invoked per method and the classes to which those method belong . As a demo for your problem , i am adding the mapping to the method invocations in the methodToInvokedMethodMap
, where key is the source class method from where method is being invoked , and value is the method which class information that is being invoked from inside method B , just like you asked in your question :) . Adding the code for the class C
, which displays this mapping as below :
import javassist.*;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class C {
static Map<String, String> methodToInvokedMethodMap = new HashMap<>();
public static void main(String[] args) throws NotFoundException, CannotCompileException {
Class thisClass = B.class;
Method[] methods = thisClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get("B");
String currentMethod = methods[i].getName().toString();
CtMethod method = ctClass.getDeclaredMethod(methods[i].getName().toString());
method.instrument(new ExprEditor() {
public void edit(MethodCall m) throws CannotCompileException {
if (m.getClassName().equals("A")) {
methodToInvokedMethodMap.put("Class B method " + currentMethod, m.getClassName() + " with name as : " + m.getMethodName());
}
}
});
methodToInvokedMethodMap.forEach((key, value) -> {
System.out.println(key + " invokes method from class " + value);
});
}
}
}
You can further explore JavaAssist library to find out different methods to serve your usecase . Also , in your Spring Boot project you can use this Jar as it is , or you can convert this jar as a Maven Dependency in your project and use it as a dependency via your pom.xml
file . Hope this helps you resolve the problem you asked for :) .
Also , i'm attaching here how the output looks like for this :
Answered By - Saurabh Chaturvedi
Answer Checked By - Mary Flores (JavaFixing Volunteer)