Reflection argument type mismatch between classloaders
For backward compatibility testing, I create my own classloader to load some of my code from the previous version. Once I have my custom object (from a custom class older), I call the API to it using reflection. However, when such an API method has its own argument (not part of the java library), for example:
public void MyMethod(MyObj a) {}
When I call this method using reflection I get:
java.lang.IllegalArgumentException: argument type mismatch
Because I am passing MyObj from the default classloader, while the method expects to get MyObj from the custom classloader.
The code I am using to call the method (while the agent was coded by my custom classloader and the arguments to the api agents obtained from my test class which was coded by the default classloader)
private Object invoke(Object... args) {
try {
final String methodName = getMethodName();
final Class<?>[] methodArgs = getMethodArgs(methodName);
return agent.getClass().getMethod(methodName, methodArgs).invoke(agent, args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
private Class<?>[] getMethodArgs(String methodName) {
final Method[] declaredMethods = agent.getClass().getDeclaredMethods();
for (Method method : declaredMethods) {
if (method.getName().equals(methodName)) {
return method.getParameterTypes();
}
}
return new Class<?>[0];
}
private String getMethodName() {
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
StackTraceElement e = stacktrace[3];
return e.getMethodName();
}
How can I solve this problem? (I cannot pass a common interface to this method because myCustomObject does not implement the interface that exists in the shared module, and I cannot add it now because old jars will not know it)
source to share
You can use your custom class loader to load the "DefaultCtorMyObjWrapper" class that extends MyObj (loaded by the custom class loader). DefaultCtorMyObjWrapper wraps the "MyObj" instance that was loaded by the default constructor (refrence must be stored as an object). DefaultCtorMyObjWrapper overrides all MyObj methods and delegates calls using reflection to the wrapped MyObj.
It should look something like this:
class DefaultCtorMyObjWrapper{
Object _defaultCtorMyObj;
public DefaultCtorMyObjWrapper(Object defaultCtorMyObj){
_defaultCtorMyObj = defaultCtorMyObj;
}
public method1(){
// invoke method1 on _defaultCtorMyObj using reflcetion
}
}
If the MyObj methods receive objects as parameters, you may need additional configuration.
source to share