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?
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;
}
As you already mentioned, you need to check every interface type for its interfaces in order to determine such a subtype-relation. This is however not difficult to do if you have access to all resources.
When you are using ASM, you simply need to take the interface names of your original class and find the class file to each such interface. You can then parse each class file for its interfaces and so on. This way, you can determine the entire graph and decide on the subtype relationship.
If you do not want to do this manually, you can use Byte Buddy which offers you methods similar to the reflection API for unloaded types:
ClassFileLocator cfl = ... // can be file system, class loader, etc.
TypePool.Default.of(cfl).describe("your.initial.type")
.resolve()
.isAssignableTo(someInterface);
You can also use the TypePool
to read the target interface, if it is not available.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With