stef
stef

Reputation: 99

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.

Upvotes: 4

Views: 1590

Answers (1)

rakwaht
rakwaht

Reputation: 3967

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;
    }
}

Upvotes: 3

Related Questions