Search code examples
java-bytecode-asm

Is there a way to tell if a class is an interface?


I'm trying to examine (at a bytecode level, ASM) classes implementing some specific interfaces (in this case, java.sql.Connection) and find that in some cases, the library has another interface extending something from my set of interfaces... and then their classes implement THAT interface. (In this case, a new extended interface com.mysql.jdbc.Connection extend java.sql.Connection and then their implementations, e.g, ConnectionImpl implement com.mysql.jdbc.Connection.) I therefore miss identifying ConnectionImpl as a target class.

So.. the result is that I need to identify com.mysql.jdbc.Connection as an 'interesting' interface when loading the class. But I can't see how to identify the class AS an interface versus just a normal class. Is there something in the ClassReader than can give me that sort of information?


Solution

  • As per the title, if you want to check if a class is an interface:

    ClassNode node = // However you wish to load the class
    if ((node.access & Opcodes.ACC_INTERFACE) != 0){
        // is interface
    } else {
        // is not an interface
    }
    

    In your post you state you wish to find children/implementations of java.sql.Connection.

    The issue you're having is this:
    java.sql.Connection -> com.mysql.jdbc.Connection -> ConnectionImpl

    ConnectionImpl does not directly implement java.sql.Connection so it's not detected as a child/implementation. For an issue like this what you would have to do is travel the class hierarchy. If I were in your situation I would load a map of ClassNodes <String, ClassNode> where the string is the ClassNode's name. Then I would use a recursive method to check if a given class is the intended type. So if you send a ClassNode in it would call itself with the node's parent and then the interfaces. Eventually if the initially given node is the correct type eventually the java.sql.Connection interface will be passed through and found. Also if you wanted to skip redundencies you could store the results of each ClassNode in a map so that you wouldn't have to check the entire hierarchy over and over again.

    Edit: Sorta like this

    public static boolean isMatch(ClassNode cn, Map<String,ClassNode> nodes, String target){
        if (cn.name.equals(target)) return true;
        else{
            if (nodes.containsKey(cn.superName) && isMatch(nodes.get(cn.superName),nodes,target)) return true;
            for (String interf : cn.interfaces){
                if (nodes.containsKey(interf) && isMatch(nodes.get(interf),nodes,target)) return true;
            }
        }
        return false;
    }