0

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.

2
  • Have you tried your createProxy function as a generic function? Commented Jul 7, 2022 at 10:14
  • 2
    It is exactly as the error message says. A is not an interface. and therefore does not satisfy the contract of createProxy(). Try it with IA. Commented Jul 7, 2022 at 10:25

1 Answer 1

2

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);
Sign up to request clarification or add additional context in comments.

1 Comment

Yes I have also seen that only for classes which implement interfaces this can be done.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.