Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

reflection argument type mismatch between class loaders

For backward compatability testing, i'm creating my own class loader to load some of my code from previous version. After I have my custom object (from the custom class older), i'm invoking the API of it using reflection. However, when such API method have custom argument (isn't part of java library), for example:

public void MyMethod(MyObj a) {}

When i'm invoking this method using reflection i'm getting:

java.lang.IllegalArgumentException: argument type mismatch

Because i'm passing MyObj from the default class loader while the method expect to get MyObj from the custom class loader.

The code I'm using to invoke the method (while agent was loded by my custom classloader and the arguments to agent's api methods arrived from my test class which loded by the default class loader)

        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 workaround this issue? (I can't pass common interface to the method because myCustomObject does not implement interface that exist in a shared module, and i can't add one now because the old jars won't know it)

like image 653
axelrod Avatar asked Nov 03 '22 04:11

axelrod


1 Answers

You can use your custom class loader to load a class "DefaultCtorMyObjWrapper" that extends MyObj (loaded by the custom class loader). DefaultCtorMyObjWrapper wraps an instance of "MyObj" that was loaded by the default constructor (the refrence should be kept as an Object). DefaultCtorMyObjWrapper overrides all of MyObj's methods and delegates invocations using reflection to the wrapped MyObj.

Should look somthing like this:

class DefaultCtorMyObjWrapper{

    Object _defaultCtorMyObj;

    public DefaultCtorMyObjWrapper(Object defaultCtorMyObj){
        _defaultCtorMyObj = defaultCtorMyObj;
    }   

    public method1(){
        // invoke method1 on _defaultCtorMyObj using reflcetion
    }

}

If MyObj's methods recieve objects as parameters you might need some more tweaking.

like image 142
Genia Avatar answered Nov 09 '22 14:11

Genia