5

How can one create a proxy for an interface without creating a class that implements it?

I have a concrete example: I have an interface, Contact, and need to create a proxy object that acts as a Contact. This proxy object will be used for running some TestNG tests.

I have tried using the JDK approach but could find only examples that needed an actual implementation of that interface.

I also found that jasssist may help me in this problem and tried implementing a simple example that seems to be working until I get an Out of Memory error. Here is a snippet of what I am doing:

import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory

protected final T createMock(final Class<T> clazz) {
    final ProxyFactory factory = new ProxyFactory();
    factory.setInterfaces(new Class[] { clazz });
    factory.setFilter(new MethodFilter() {
        public final boolean isHandled(final Method m) {
            // ignore finalize()
            return !m.getName().equals("finalize");
        }
    });

    final MethodHandler handler = createDefaultMethodHandler();
    try {
        return (T) factory.create(new Class<?>[0], new Object[0], handler);
    } catch (final Exception e) {
        e.printStackTrace();
    }
    return null;
}
private MethodHandler createDefaultMethodHandler() {
    return new MethodHandler() {
        public final Object invoke(final Object self,
                final Method thisMethod, final Method proceed,
                final Object[] args) throws Throwable {
            System.out.println("Handling " + thisMethod
                    + " via the method handler");
            return thisMethod.invoke(self, args);
        }
    };
}

Remember that the parameter of the createMock() method will be an interface.

Thanks

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
virgium03
  • 627
  • 1
  • 5
  • 14
  • Why do you ignore calls to finalize? Maybe that has something to do with your OutOfMemory exceptions!? – Arne Deutsch Oct 05 '10 at 12:48
  • The error still occurs if I take into consideration the finalize() method. I think the problem is somewhere else, because I get the same errors when using javassist and commons-proxy. For the moment, I think I will provide an simple implementation for each interface that needs to be tested because I invested some time into this problem and could make it work. – virgium03 Oct 05 '10 at 12:56

5 Answers5

8

You can use the method newProxyInstance of java.lang.reflect.Proxy. Ex:

    Proxy.newProxyInstance(iClazz.getClassLoader(),
        new Class[]{iClazz},
        new YourInvocationHandler())

iClazz is the class of your interface and YourInvocationHandler is an instance of java.lang.reflect.InvocationHandler

eboix
  • 5,113
  • 1
  • 27
  • 38
Bateramos
  • 136
  • 1
  • 6
2

commons-proxy aims at simplifying the task.

What you want is an invoker proxy (without a target object). So you can use:

ProxyFactory factory = new JavassistProxyFactory();
Object result = 
      factory.createInvokerProxy(invoker, new Class[] {YourInterface.class});

And your invoker must implement the Invoker interface, whose invoke method will be called on each method invocation. (4 times the word "invoke" here)

Note that commons-proxy uses the preferred underlying proxying mechanism - in the above example it's javassist.


However, you seem to need the proxy for mocking purposes. With mockito this is as easy as:

YourInterface yourMock = mock(YourInterface.class);
when(yourMock.someMethod()).thenReturn(yourPreferredResult);
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • i will try the commons proxy. note that my provided snippet works but the tests take very long to run, throw a lot of InvocationTargetExceptions and then OutOfMemory. – virgium03 Oct 05 '10 at 10:59
1

If you are only interesting in mocking I would suggest to use a framework.

EasyMock ( http://easymock.org/ ) or JMock ( http://www.jmock.org/ ) might fit.

To create a proxy yourself you can use the class java.lang.reflect.Proxy.

Arne Deutsch
  • 14,629
  • 5
  • 53
  • 72
  • I looked at the mocking frameworks and tried EasyMock but I just wanted to create a proxy and not set some expectations, behavior and so on. – virgium03 Oct 05 '10 at 10:52
0

Exampledepot has a simple snippet for how to create an interface proxy based on java.lang.reflect.proxy at http://exampledepot.8waytrips.com/egs/java.lang.reflect/ProxyClass.html

You just need to provide functionality for the invoke(Object proxy, Method m, Object[] args) in your own code.


EDIT: Based on comments it appears that the class you do not want is the interface class, not the implementation class. In that case there is no way around byte code manipulation. You may want to look at Javassist which contains a snippet compiler.

Thorbjørn Ravn Andersen
  • 73,784
  • 33
  • 194
  • 347
0

In your code i did the following

//              return thisMethod.invoke(self, args);
            return null;

and i got the following result

Handling public abstract void org.rege.instruments.IPerson.setName(java.lang.String) via the method handler

Is this what did you wanted? As you can see the OutOfMemory was produced by a recursive call to your invoke.