joe_specimen
joe_specimen

Reputation: 355

Exception java.lang.IllegalArgumentException: is not an interface when using dynamic proxies

I want to implement a generic functionality which would enable that our domain classes are being proxied for the case that its values to be xml compliant (escaping special characters in strings). The domain classes/objects are being generated, so that they can not be changed by me. What I tried to do was following code for the generation of proxies:

public class StringValuesFormatterForXml implements InvocationHandler {

    public static interface IA {
        String getMa1();

        List<? extends IB> getBs();
    }

    public static class A implements IA {

        @Override
        public String getMa1() {
            return "Ma1";
        }

        @Override
        public List<? extends IB> getBs() {
            return Arrays.asList(new B(), new B());
        }
    }

    public static interface IB {
        String getMb1();

        String getMb2();
    }

    public static class B implements IB {
        @Override
        public String getMb1() {
            return "Mb1";
        }

        @Override
        public String getMb2() {
            return "Mb2";
        }
    }

    Object destObj;

    private final Map<String, Method> methods = new HashMap<>();

    @SuppressWarnings("unchecked")
    public static IA createProxyA(IA destObj) {
        return (IA) Proxy.newProxyInstance(destObj.getClass().getClassLoader(), new Class[] {
                IA.class
        }, new StringValuesFormatterForXml(destObj));
    }

    @SuppressWarnings("unchecked")
    public static Object createProxy(Object destObj, Class<?> clazz) {
        return Proxy.newProxyInstance(destObj.getClass().getClassLoader(), new Class[] {
                clazz
        }, new StringValuesFormatterForXml(destObj));
    }

    public StringValuesFormatterForXml(Object destObj) {
        this.destObj = destObj;

        for (Method method : destObj.getClass().getMethods()) {
            this.methods.put(method.getName(), method);
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getReturnType().isAssignableFrom(List.class)) {
            List<Object> elems = (List<Object>) method.invoke(destObj, args);
            List<Object> proxyElems = new ArrayList<Object>();
            for (Object obj : elems) {
                Object proxyObj = createProxy(obj, obj.getClass());
                proxyElems.add(proxyObj);
            }

            return proxyElems;
        }
        return method.invoke(destObj, args); // Here I will format the output for xml
    }

    public static void main(String[] args) {
        A orig = new A();
        IA proxy1 = createProxyA(orig);
        A proxy2 = (A) createProxy(orig, orig.getClass());
    }

}

Code in createProxy(orig, orig.getClass()) throws following error java.lang.IllegalArgumentException: StringValuesFormatterForXml$A is not an interface but the code createProxyA(orig) does not. So it seems that I would need to have a separate creator method for every interface which I use. In our domain model there are many classes and I do not want to create for every class separate creator. Are there any other ways/frameworks which are better suited for my case of proxying objects.

Upvotes: 0

Views: 1384

Answers (1)

Japhei
Japhei

Reputation: 645

Your createProxy method does work, you just have to pass the class of the interface as second parameter:

A orig = new A();
IA proxy1 = (IA) createProxy(orig, IA.class);

In addition I would recomment you to use the createProxy function as a generic function in order to avoid the obkect cast:

@SuppressWarnings("unchecked")
public static <T> T createProxyB(T destObj, Class<T> clazz) {
    return (T) Proxy.newProxyInstance(destObj.getClass().getClassLoader(), new Class[] { clazz },
            new StringValuesFormatterForXml(destObj));
}

In this case you can call the function like this:

A orig = new A();
IA proxy1 = createProxyB(orig, IA.class);

Upvotes: 2

Related Questions