-2

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?

Nick Mckenna
  • 125
  • 1
  • 6

2 Answers2

1

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);
        }
    }


}
user207421
  • 305,947
  • 44
  • 307
  • 483
minus
  • 2,646
  • 15
  • 18
  • RMI serializes proxies every day of the week. Would have upticked you if not for that. You just have to add `Serializable` to the list of implemented interfaces. – user207421 Jul 05 '17 at 00:31
  • I think you are mixing things up. RMI does the opposite. You need a concrete class and a interface to receive, then RMI does the dynamic proxy trick to stick the implementation on the interface. But you cannot start from a dynamic proxy. – minus Jul 05 '17 at 00:38
  • I am not mixing anything up. When you *have* the dynamic proxy, as the result of the export step, RMI serializes it, e.g. to the Registry. If you couldn't serialise proxies, RMI couldn't use them. Try it before you debate this further. – user207421 Jul 05 '17 at 00:44
  • 1
    You live and learn ... never thought you could serialize such an exotic object as a Proxy. I was wrong. I must thank you, that's one of the reasons I like SO, you learn a lot, even when replying to question. – minus Jul 05 '17 at 01:23
0

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.

Samuel
  • 16,923
  • 6
  • 62
  • 75