Mocking final classes with BCEL, java.lang.instrument and EasyMock

I've introduced EasyMock library in previous entry of testing category. I read in documentation of EasyMock that final methods cannot be mocked. I've found solution of this problem for testing with Jmock library. Bytecode of the class is changed before the class is loaded. The solution also works for EasyMock. I'll describe how to do it in next paragraphs.

The Byte Code Engineering Library (BCEL) library provide simple way how to navigate and change bytecode of java classes. I used this library in order to remove final flag from classes. It is only few lines by using this library.

Java.lang.instrument provides services that allow Java programming language agents to instrument programs running on the JVM. Nice article about it is here. I used java.lang.instrumnet for changing bytecode during classloading.

Example with removing final flag is below. Transf class is implementation of java.lang.instrument service. It is not difficult to add removing of final flag for methods of class to code. I created this example to demonstrate how powerful is java.lang.instrument service.

 
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
....

public class Transf implements ClassFileTransformer {
	public byte[] transform(ClassLoader loader, 
                                String className, 
                                Class redefiningClass, 
                                ProtectionDomain domain, 
                                byte[] bytes) throws IllegalClassFormatException {
                try {
                    ClassParser parser = new ClassParser(new ByteArrayInputStream(bytes),className);
                    JavaClass clazz = parser.parse();
                    clazz.setAccessFlags(clazz.getAccessFlags() & (~Constants.ACC_FINAL ));
                    bytes = clazz.getBytes();
                    System.out.println("Transformed Class: " + className);
                } catch (ClassFormatError ex) {
                    ex.printStackTrace();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            return bytes;
	}
}

To initialize the class patching agent it is necessary to create premain class:

public class SimpleMain {
	public static void premain(String agentArguments, Instrumentation instrumentation) {	
		instrumentation.addTransformer(new Transf());
	}	
}
The referece of the premain class is specified in MANIFEST.mf of build jar with java agent.
 
Manifest-Version: 1.0 
Premain-Class: mypackage.SimpleMain
When we run tests we need to say java where the java agent service is located by using -javaagent parameter.
java -javaagent:classpatching.jar ...
Comments:

Post a Comment:
Comments are closed for this entry.
About

xzajo

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today