1
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
YasSir
  • 15
  • 2
  • Presumably it knows the name but doesn't have the full definition. Not enough info in your question to say why that is. – Michael Jun 28 '22 at 14:28
  • 2
    `org.apache.bcel.classfile.JavaClass#getSuperClass` will try to load the class by the name (String) from `getSuperclassName` from it's repository. And the error message is telling you that it can't find that class. Please update your question with your bcel setup (how are you loading javaClass) and the runtime classpath that gives this error. – slindenau Jun 28 '22 at 14:43
  • basically the code process jar files and iterate over all classes. when processing a class I want to extract its super class. something like this : [link](https://github.com/gousiosg/java-callgraph/blob/master/src/main/java/gr/gousiosg/javacg/stat/ClassVisitor.java). In the visitJavaClass method I want to extract the super class of the jc. @slindenau – YasSir Jun 28 '22 at 20:41
  • What is `javaClass` and where did it come from? – user207421 Jun 29 '22 at 03:57
  • Additional questions: where are `getSuperclassName()` and `getSuperClass()` defined? Neither of them is defined on [`java.lang.Class`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html). There is a close match – [`getSuperclass()`](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getSuperclass--) but that isn't the same – upper case "C" vs. lowercase "c". – Kaan Jun 29 '22 at 04:08

1 Answers1

1

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.

slindenau
  • 1,091
  • 2
  • 11
  • 18