Serializing a proxy

1.4k views Asked by At

I am trying to serialize an invocation handler to a file. I am only trying to serialize the following part as it is the only part of the program that will change:

 public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        System.out.println("BEFORE");
        method.invoke(original, args);
        System.out.println("AFTER");
        //System.out.println(method);
        return null;

    }

I get the following error run:Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - Erroneous tree type: <any> at jdkproxydemo.JdkProxyDemo.main(JdkProxyDemo.java:69) C:\Users\ACK\AppData\Local\NetBeans\Cache\8.1\executor-snippets\run.xml:53: Java returned: 1 BUILD FAILED (total time: 1 second)

Here is the full code:

public class JdkProxyDemo {


interface If {
    int originalMethod(String s);
}

static class Original implements If {
    public int originalMethod(String s) {
        System.out.println(s);
        return 0;
    }
}

public static class Handler implements InvocationHandler, Serializable {

    private final If original;

    public Handler(If original) {
        this.original = original;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        System.out.println("BEFORE");
        method.invoke(original, args);
        System.out.println("AFTER");
        //System.out.println(method);
        return null;

    }
}

public static void main(String[] args) throws FileNotFoundException, IOException{

      ///  OutputStream file = null;

        Original original = new Original();
        Handler handler = new Handler(original);
        If f = (If) Proxy.newProxyInstance(If.class.getClassLoader(),new Class[] { If.class },handler);
                OutputStream file = new FileOutputStream("quarks.ser");
  OutputStream buffer = new BufferedOutputStream(file);
   ObjectOutput output = new ObjectOutputStream(buffer);
   output.writeObject(handler.invoke(f,handler.original,"a"));
   output.close();    
}

}

What is the best way to achieve this result, serializing a proxy object?

2

There are 2 answers

0
Samuel On

Your code is currently trying to serialize private final If original because it's part of Handler. However the If interface does not extend Serializable, so you cannot serialize Handler. You should either change to

interface If extends Serializable

Or if that is not possible, you should use a different serialization method than Java serialization.

4
minus On

Let's check this:

output.writeObject(handler.invoke(f,handler.original,"a"));

Here you are not serializing the proxy f but the result of a method call:

handler.invoke(f,handler.original,"a")

Also the method invoke requires an array of arguments, so you need to pass an array not a string.

handler.invoke(f,handler.original,new Object[]{"a"})

And handler.original is not a method ... it should be If.class.getMethods()[0]

handler.invoke(f,If.class.getMethods()[0],new Object[]{"a"})

But since it is a proxy of If it could be written:

f.originalMethod("a")

But method returns always null:

public Object invoke(Object proxy, Method method, Object[] args) {
    // [...]
    return null;
}

So in fact you are trying to serialize null which is pointless.

EDIT (after comment)

The proxy can be serialized provided only that the handler is itself serializable.

public interface Adder {

    int add(int x, int y);
}

The handler must be a Serializable object

public class AdderHandler implements InvocationHandler, Serializable {

    private int factor;

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        int a = (Integer) args[0];
        int b = (Integer) args[1];
        return (a+b)*factor;
    }

    public static Adder newInstance(int factor){
        Class[] clazz = new Class[]{Adder.class};
        AdderHandler h = new AdderHandler(factor);

        return (Adder) Proxy.newProxyInstance(AdderHandler.class.getClassLoader(), clazz, h);
    }

    public AdderHandler(int factor) {
        super();
        this.factor = factor;
    }
}

Since the Proxy has only 1 field (the handler) ...

public class TestAdder {

    @Test
    public void testSerialization() throws Exception {
        Adder adder = AdderHandler.newInstance(2);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try (ObjectOutputStream oos = new ObjectOutputStream(bos);){
            oos.writeObject(adder);
        }

        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        try(ObjectInputStream ois = new ObjectInputStream(bis);){

            Adder adder2 = (Adder) ois.readObject();
            int result = adder2.add(1, 3);
            assertEquals(8, result);
        }
    }


}