Question

This is a bit tricky to explain. I have a class A:

public class A {
    private Integer a1;
    private Integer a2;
    // getters and setters.
}

There is a static class B that returns my class A:

public static class B {
    public static A getCurrentA() {
        return a;
    }
}

I need to find all usages of class A returned by B. So let's say class C calls c.setA(B.getCurrentA()) and then further along there's a call to c.getA().getA2();, I'd want to find all of these.

In the real scenario, I have 217 different classes that call B.getCurrentA(). I can't manually follow all the calls in Eclipse and find out which methods are getting called.

Eclipse call hierarchy view only shows me all calls to B.getCurrentA().

How can I achieve this?


EDIT

Chris Hayes understood what I want to do. In order to refactor some really bad legacy code without breaking the whole system, I need to first fine-tune some queries using Hibernate's projections (every mapped entity in the system is eagerly loaded, and many entities are related, so some queries take a LONG time fetching everything). But first I need to find which properties are used so that I don't get a NullPointerException somewhere...

Here's an example of what I'd have to do manually:

  1. Use Eclipse's Search to find all calls to B.getCurrentA();
  2. Open the first method found, let's say it's the one below:

    public class CController {
        C c = new C();
        CFacade facade = new CFacade();
        List<C> Cs = new ArrayList<C>();
    
        public void getAllCs() {
            c.setA(B.getCurrentA()); // found it!
            facade.search(c);
        }
    }
    
  3. Open the search method in the CFacade class:

    public class CFacade {
        CBusinessObject cBo = new CBusinessObject();
    
        public List<C> search(C c) {
            // doing stuff...
            cBo.verifyA(c);
            cBo.search(c); // yes, the system is that complicated
        }
    }
    
  4. Open the verifyA method in the CBusinessObject class and identify that field a2 is used:

    public class CBusinessObject {
        public void verifyA(c) {
            if (Integer.valueOf(1).equals(c.getA().getA2())) {
                // do stuff
            else {
                // something else
            }
        }
    }
    
  5. Repeat steps 2-4 for the next 216 matches... Yay.

Please help.

Was it helpful?

Solution

If you want to make any source code changes/refactoring you will have to manually find all usages and apply your code changes;

Any way, I have two different aproach

  1. Static search You can simply do Text Search in eclipse to find the occurance of getA2() . It will directly take you to the Caller method (here CBusinessObject.verifyA()) -but it will give you every getA2() occurances, may be from different class

  2. Run time search Use java instrumentation API to change the byte code at run time on your required method to find invoking class and run as java agent - Enable you to identify the caller with out touching the existing code base and very useful especially when you don't have access to source code.

Here you go how to implement

Step 1- Write Agent main class to initiate instrumentation

public class BasicAgent {
                public static void premain(String agentArguments, Instrumentation instrumentation){
                    System.out.println("Simple Agent");
                    FindUsageTransformer transformer = new FindUsageTransformer ();
                    instrumentation.addTransformer(transformer,true);
                }
            }

Step 2 -Write a ClassFileTransformer implementation and capture the method

public class FindUsageTransformer implements ClassFileTransformer{

        Class clazz = null;
        public byte[] transform(ClassLoader loader,String className,Class<?>  classBeingRedefined,  ProtectionDomain    protectionDomain,
                byte[]              classfileBuffer)    throws IllegalClassFormatException {
            if(className.equals("A")){
                doClass(className, classBeingRedefined, classfileBuffer);
            }
            return classfileBuffer;
        }
        private byte[] doClass(String name, Class clazz, byte[] b) {
            ClassPool pool = ClassPool.getDefault();
            CtClass cl = null;
            try {
              cl = pool.makeClass(new java.io.ByteArrayInputStream(b));
              CtMethod method =  cl.getDeclaredMethod("getA2");
              // here you have lot of options to explore
              method.insertBefore("System.out.println(Thread.currentThread().getStackTrace()[0].getClassName()+ Thread.currentThread().getStackTrace()[0].getMethodName());");
              b = cl.toBytecode();
            } catch (Exception e) {
              System.err.println("Could not instrument  " + name
                  + ",  exception : " + e.getMessage());
            } finally {
              if (cl != null) {
                cl.detach();
              }
            }
            return b;
          }

Step 3- create jar file for agent classes ( you have to set manifest file with premain class, and add javaassit jar) snippet of build file is given - you can do it by manually as well

<jar destfile="build/jar/BasicAgent.jar" basedir="build/classes">
                <manifest>
                    <attribute name="Manifest-Version" value="1.0"/>
                    <attribute name="Premain-Class" value="com.sk.agent.basic.BasicAgent"/>
                    <attribute name="Boot-Class-Path" value="../lib/javassist.jar"/>
                </manifest>
            </jar>

Step 4- Run your main application with java agent - before that set VM arguments to load agent

            -`javaagent:D:\softwares\AgentProject\AgentLib\build\jar\BasicAgent.jar`

Pre requisite : you would need javassist.jar in the class path.

OTHER TIPS

Depending on the IDE you are using this problem is simpler to find.

Eclipse IDE has one of the most potential Call Hierarchy modules existing, you just need to put the mouse in the method declaration that you want to find and execute Ctrl + Alt + H This will give you the entire hierarchy of which method is using the method you want to analyze.

Also the Call Hierarchy module offers a mode where you can find the methods that your method is calling.

Some extra info: http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.cdt.doc.user%2Freference%2Fcdt_u_call_hierarchy_view.htm

In IntelliJ IDEA, if you want to find usages of c.getA().getA2(); right-click on A.a2 and choose "find usages." Similarly for A.a1 and B.getCurrentA(). Unused fields and methods show up in a different color in IDEA. I've heard that IntelliJ has more refactoring power than Eclipse, but I bet Eclipse does the same thing, just slightly differently.

Also, using grep, find, and sed, you can search for the appropriate methods, just in files that are in the same package as A or that import A, or spell it out by name.

I hope I understood your question correctly. I think you can use grep -Irns function to find the calls. You can grep for getA().getA2(). That will return lines from where functions are called along with line numbers.

Rather than scanning for all references to the method getCurrentA do a scan for all references to the Class A.

This will show you everywhere that class is used within your program and you will probably find it is easier to go through and scan that list by hand and decide if you need to act on each result found than trying to do anything fancy.

The easiest way to find Call Usage is using references in eclipse,but there is a funny way :

  1. Change method name to B.getCurrentAA()
  2. Build your Project
  3. Your Project compiles with error
  4. Go to Marks Part and see usage Error And Find Usage Of your method

I think IntelliJ can solve your problem. It have an "Analyze dataflow" feature and I think it is doing what you are looking for:

Here is my sample code:

public class Main {

    private static A a = new A();  //nevermind the way it is initialized

    public static A getA(){
        return a;
    }

    public void method(){
        A myA = getA();
        Integer a1 = myA.getA1();  //this line is found
        Integer a2 = myA.getA2();  //this line is found
    }

    public void anotherMethod(){
        A myA = new A();         
        Integer a1 = myA.getA1();  //this line is NOT found
        Integer a2 = myA.getA2();  //this line is NOT found
    }
}

Running the "Analyze dataflow from here" (with cursor on return a; line) give me this:

enter image description here

Sorry to provide you only a solution with IntelliJ (tested with IntelliJ-13 Ultimate Edition)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top