views:

111

answers:

4

Hello,

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

A: 

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
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
+1  A: 

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
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
A: 

Exampledepot has a simple snippet for how to create an interface proxy based on java.lang.reflect.proxy at http://www.exampledepot.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
thanks, but that example needs an implementation of the interface and I do not want to create an actual implementation.
virgium03
this is what I am trying to avoid.
virgium03
A: 

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.

Stahis Alexopoulos