Saturday May 13, 2006 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.SimpleMainWhen we run tests we need to say java where the java agent service is located by using -javaagent parameter.
java -javaagent:classpatching.jar ...Posted by xzajo ( May 13 2006, 03:00:00 PM CEST ) Permalink Comments [7]
SourceForge - CVS server down for a week and up with different host name!
CVS server is many times down. The last was on the end of last week. The server is up again today. But with a small difference. The host name of my cvs server has been chaged. What's mean for me? I was developing for a week without commit. I will have to checkout the cvs repository to other folder on my local disk. Copy changed files from old cvs checkout to new folder. SourceForge have many other problems. I started lookong for new opensource provider. When I find them I'll move the nbxdoclet project.
Posted by xzajo ( May 13 2006, 01:10:23 AM CEST ) Permalink Comments [10]