ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Analyze Your Classes
Pages: 1, 2, 3

Class2HTML

This is the utility that traverses a class file and creates five HTML files. These HTML files completely describe the class file by dividing it into constant pool, attributes, byte code, and methods. The fifth file combines all of these into one easy-to-use HTML file.



Run Class2HTML on HelloWorld.class as shown below:

java org.apache.bcel.util.Class2HTML HelloWorld.class

This will create five files in the current directory. Open HelloWorld.html in your browser to see the class contents as illustrated in Figure 1:

Figure 1. HelloWorld.html
Figure 1. HelloWorld.html, as generated by Class2HTML

I have marked the frames into the corresponding class file elements. This is a quick and easy way to map out a class file with the Class2HTML utility.

Static API

Using the static API, let's implement a simple class viewer. This is a simple example that exercises the JavaClass class and is similar to how Class2HTML operates.

import org.apache.bcel.Repository;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.classfile.JavaClass;  
public class ClassViewer{
   private JavaClass clazz;
   public ClassViewer(String clazz){
      this.clazz = Repository.lookupClass(clazz);
    }
   public static void main(String args[]){
      if(args.length != 1)
        throw new IllegalArgumentException(
          "One and only one class at a time!");
      ClassViewer viewer = new ClassViewer(args[0]);
      viewer.start();
    }
   private void start(){
      if(this.clazz != null){
        // first print the structure 
        // of the class file
        System.err.println(clazz);
        // next print the methods
        Method[] methods = clazz.getMethods();
       for(int i=0; i<methods.length; i++){
          System.err.println(methods[i]);
          // now print the actual
          // byte code for each method
          Code code = methods[i].getCode();
         if(code != null)
            System.err.println(code);
       }
     }else
        throw new RuntimeException(
          "Class file is null!");
    }
}

The first thing that this example does is to look up the class that is to be mapped by requesting that the Repository load it. This is done by the Repository.lookupClass(String classname) method. The repository loads this class as a JavaClass that contains all of the information in the Java Class file format. From then on, it is a simple matter of printing the class file structure using the toString conversion on the JavaClass file and the methods and code.

Dynamic API

We have already seen the code required to create a dynamic class when we visited the BCELifier example. HelloWorldCreator.java creates a dynamic class on the fly. Let us see how we can modify this class dynamically by adding a new method.

import org.apache.bcel.*;
import org.apache.bcel.generic.*;
import org.apache.bcel.classfile.*;  
public class ClassModifier implements Constants{
 private JavaClass clazz;
 private ClassGen classGen;
 private ConstantPoolGen cp;
 public ClassModifier(String clazz){
  this.clazz = Repository.lookupClass(clazz);
  this.classGen = new ClassGen(this.clazz);
  this.cp = this.classGen.getConstantPool();
 }
 public static void main(String args[]){
  if(args.length != 1)
   throw new IllegalArgumentException(
      "One and only one class at a time!");
  ClassModifier modifier = new ClassModifier(args[0]);
  modifier.start();
 }
 private void start(){
  if(this.clazz != null) {
   // print the methods BEFORE adding the new one
   Method[] methods =
    classGen.getJavaClass().getMethods();
   System.err.println(
    "++++ Before adding new method ++++");
   for(int i=0; i<methods.length; i++){
    System.err.println(methods[i]);
   }
  InstructionList il = new InstructionList();
  classGen.addMethod(
    new MethodGen (ACC_PUBLIC | ACC_STATIC,
                   Type.VOID,
                   Type.NO_ARGS,
                   new String[] { },
                   "newMethod",
                   clazz.getClassName(),
                   IL,
                   cp).getMethod());
   // print the methods AFTER adding the new one
   methods = classGen.getJavaClass().getMethods();
   System.err.println(
    "\n++++ After adding new method ++++");
   for(int i=0; i<methods.length; i++){
    System.err.println(methods[i]);
   }
  } else
   throw new RuntimeException("Class file is null!");
  }
}

This class loads a class file and represents it in the memory using JavaClass. This part is similar to what I did in the static ClassViewer example. Having created this representation in memory of the input class, the code above creates an instance of the ClassGen class, using this representation as the base:

this.classGen = new ClassGen(this.clazz);
this.cp = this.classGen.getConstantPool();

A new method is then added to this instance of classGen by using the MethodGen constructor. As you can see, this new method has the access flags of public and static and is called newMethod. The rest of the code simply prints a list of methods in the class before and after the method is added.

Run this code using HelloWorld.class as the input class file. You will see the following output:

++++ Before adding new method ++++
public void <init>()
public static void main(String[] arg0)
++++ After adding new method ++++
public void <init>()
public static void main(String[] arg0)
public static void newMethod()

As you can see, the newMethod is added to our class file dynamically without having to touch the source code.

This has been a superficial treatment of the dynamic part of the API. The idea is to improve performance and add enhancements by being able to dynamically modify class files and not just add trivial methods.

Resources

  1. The Java language specification is a must-read if you want to know more about the internal workings of the JVM and understand the class file structure.
  2. The BCEL home page contains a manual that gives a general introduction to the API.
  3. The mailing lists are a great place to ask experienced developers questions about the API.
  4. Finally, to really understand how BCEL operates, download the source code and start with a look at the utilities package. The utilities contain a good amount of code that exercise this API.

Vikram Goyal is the author of Pro Java ME MMAPI.


Return to ONJava.com.