Search code examples
javasuperclassbcel

getSuperClass() throws Exception but getSuperclassName() returns the name of the super class


System.out.println(javaClass.getSuperclassName());

JavaClass javaClass1 = javaClass.getSuperClass();

the first line output the name of the class: RestController

The second line throws Exception:

java.lang.ClassNotFoundException: Exception while looking for class example.RestController: java.io.IOException: Couldn't find: example/RestController.class

Solution

  • So, you're using The Byte Code Engineering Library (Apache Commons BCEL™) to load classes from a Jar file (by filename) and want to print the entire call graph with the following github project.

    This works just fine, until you want to ask for the superclass of a class that was found in your jar file.

    So bcel will load a .class file, and store all it can read from the class file in a JavaClass model. This is for example the name, some flags, the super class name, the declared methods etc.

    For example inspect the following java code;

    JavaClass stringClass = Repository.lookupClass("java.lang.String");
    System.out.println(stringClass);
    

    with output:

    public final class java.lang.String extends java.lang.Object
    implements      java.io.Serializable, java.lang.Comparable, java.lang.CharSequence
    file name       java.lang.String
    compiled from       String.java
    compiler version    52.0
    access flags        49
    constant pool       540 entries
    ACC_SUPER flag      true
    
    Attribute(s):
        SourceFile: String.java
    etc...
    

    So bcel knows that the superclass is java.lang.Object, but it has not loaded any of the classes at this point! For JRE classes this is of course moot, but for the classes from your Jar file this is a problem.

    Because org.apache.bcel.classfile.JavaClass#getSuperclassName will just return the String value that it found as the super class in the .class file. Again this class was not loaded, so the Repository doesn't know about it.

    When you then ask for the org.apache.bcel.classfile.JavaClass#getSuperClass, it will try to find it like so:

        public JavaClass getSuperClass() throws ClassNotFoundException {
            return "java.lang.Object".equals(this.getClassName()) ? null : this.repository.loadClass(this.getSuperclassName());
        }
    

    Bcel will try to load it from its Respository, and if the class is unknown, it will delegate the loading to the current ClassPath. Since you're just inputting a File pointing to a Jar, this will fail with the ClassNotFoundException.

    There are two ways to you can solve this:

    1. Put the jar file(s) on your classpath; for example via Including all the jars in a directory within the Java classpath

    2. First load all the jar files into the Repository of bcel, so that it knows these classes exist. If we stay in the JCallGraph example from github, that would look something like this:

    // ... JCallGraph code from Github above this point
    
                try (JarFile jar = new JarFile(f)) {
    // extra stream over jar entries
                    Stream<JarEntry> jarEntryStream = enumerationAsStream(jar.entries());
    
                    jarEntryStream.filter(jarEntry -> jarEntry.getName().endsWith(".class"))
                    .forEach(jarEntry -> {
                        ClassParser cp = new ClassParser(jarFileName, jarEntry.getName());
                        try {
    // here we tell BCEL this class exists
                            Repository.addClass(cp.parse());
                        } catch (IOException ex) {
                            throw new RuntimeException(ex);
                        }
                    });
    
    // ... back to the JCallGraph code from Github
                    Stream<JarEntry> entries = enumerationAsStream(jar.entries());
    

    Note that if you have multiple jar files, or super classes coming from external dependencies, these all need to be on the classpath (1) or loaded in bcel first (2) before you can load the superclass.