Thursday, September 24, 2009

Modify javap to output to String, view decompiled information at runtime.

I asked the following question on Stackoverflow:

"I am working with an application webserver and have complicated classloading issues. I want to be able to download or print information that would normally get printed by javap (the methods, etc).
I may also need to get the raw binary class data, to perform a binary diff.
How would you do this?"

I was not satisfied with the responses from stack. Sure, I could have used reflection to print information on a particular class from the web, but I was looking for the output from javap.

Javap is essentially a decompiler that comes with Sun's set of Java tools, it is used to print the method names and other information for an associated class.

It is a useful tool, how do you run the tool on code that is deployed on a server, at runtime?

It seemed obvious that javap was written in Java, so I naturally assumed that the source was available with openjdk. Source for javap is available GPL licensed in the mercurial repository. There is no use of the java.lang.Reflection API, all data collected on the class is based on the binary content of the class.

I used the source to add small modifications to run javap in a web environment, simply converting the data to a string.

I made two major changes to extract the class information dynamically. One: return the current thread, current classloader and then find the class resource. Two: instantiate a bytearrayoutputstream object and use the outputstream to write the javap information. Then use the bytearrayoutputstream to convert the data to a string object. After performing those steps, use the string data in any environment you need to.
public void displayResults(InputStream classinMem) {
final InputStream classin = classinMem;
try {
// actual do display
JavapPrinter printer = new JavapPrinter(classin, out, env);

} catch (IllegalArgumentException exc) {
public void entry(String[] argv) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
this.out = new PrintWriter(baos);
try {
} finally {
} // End of the try - Catch Finally //
public void perform() {
final InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream(classNameParm);
if (input == null) {
throw new IllegalStateException("Invalid class resource, classname=" + classNameParm);

Shown in the screenshot below, we get similar output as String content.

Download the Full Source
Download the source, tested with Java 1.5 compiler and runtime.

Disclaimer: I didn't have time to re-compile the code after some changes. So, you may have to make obvious edits to get all of the code to work. And the build.xml is not working.


No comments: