Search code examples
javamethodscode-injectionjavassistjavaagents

Java javassist method calling


So I'm using java agent with javassist for purpose of injecting some small monitoring related code to different methods in different classes.

My java agent code:

public class ConverterAgent implements ClassFileTransformer {

public static void premain(String args, Instrumentation instrumentation){
    System.out.println(">>>>>>>>>> Intializing Java agent <<<<<<<<<<");
    ConverterAgent transformer = new ConverterAgent();
    instrumentation.addTransformer(transformer);

}

public static void agentmain(String args, Instrumentation instrumentation){
    System.out.println(">>>>>>>>>> Intializing Java agent <<<<<<<<<<");
    ConverterAgent transformer=new ConverterAgent();
    instrumentation.addTransformer(transformer); 
}


@Override
public byte[] transform(final ClassLoader loader, 
        String className, 
        Class<?> classBeingRedefined, 
        ProtectionDomain protectionDoman, 
        byte[] classFileBuffer)
                throws IllegalClassFormatException {



//javassist code goes here


return classFileBuffer;

}

}

And my javassist injections look like this:

if ("className1".equals(className)){

//code

}


 if ("className2".equals(className)){

//same code as in first class

}


if ("className3".equals(className)){

//same code as in first and second class

}

So I'm injecting the same exact code multiple times, I want to optimize my process and call a method for every injection so I don't have to copy same code all over and over again. But here's where I hit my problem, what Method Type am I supposed to use and what arguments does it need besides Class and Method names.


Solution

  • Into you transform method you are taking the class bytecode and then returning the "new" class bytecode with your modifications.

    This means that for sure what you are going to return is the byte[] containing the information needed into the transform method.

    So your method should be something like that:

    public class DynamicTransformer implements ClassFileTransformer {
    
        public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined,
        ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
    
            byte[] byteCode = classfileBuffer;
    
            // into the transformer will arrive every class loaded so you filter 
            // to match only what you need
            if (className.equals("com/full/path/to/be/instrumented/className1") ||
                className.equals("com/full/path/to/be/instrumented/className2") ||
                className.equals("com/full/path/to/be/instrumented/className3") ) {
    
                byteCode = myMethodThatTransform(className, byteCode);
            }
    
            return byteCode;
        }
    
    
        public byte[] myMethodThatTransform(String className, byte[] byteCode){\
            try {
                // retrive default Javassist class pool
                ClassPool cp = ClassPool.getDefault();
                // get from the class pool our class with this qualified name
                CtClass cc = cp.get(className);
                // get all the methods of the retrieved class
                CtMethod[] methods = cc.getDeclaredMethods()
                for(CtMethod meth : methods) {
                    // The instrumentation code to be returned and injected
                    final StringBuffer buffer = new StringBuffer();
                    String name = meth.getName();
                    // just print into the buffer a log for example
                    buffer.append("System.out.println(\"Method " + name + " executed\" );");
                    meth.insertBefore(buffer.toString())
                }
                // create the byteclode of the class
                byteCode = cc.toBytecode();
                // remove the CtClass from the ClassPool
                cc.detach();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return byteCode;
        }
    }