Proxy SPI
jaikiran Feb 12, 2010 5:46 AMBased on our discussion yesterday about a need for a proxy-spi, here's what my initial thoughts are. The following SPI keeps in mind both nointerface view (which uses Javassist) and the other business view (which use j.l.r.Proxy). My initial thoughts around this were, to have a ProxyFactory which at the minimum has this: {code:java} public interface ProxyFactory { /** * Creates a proxy which is castable to the interfaces passed and associates * the passed invocationHanlder with the proxy */ Object createProxy(Class[] interfaces, InvocationHandler invocationHandler); ... } {code} With this, we could then have had a JavaReflectProxyFactory which would return Proxy.newInstance(...) and a JavassistProxyFactory which would do its own proxy creation logic. Note that currently the Javassist proxy factory for nointerface view uses a j.l.r.InvocationHandler, but that's a implementation detail, and as such should not be exposed through the SPI. i.e. the createProxy shouldn't ideally be expecting a j.l.r.InvocationHandler as a param. Also, if other proxy factory implementations need some more contextutal information for creation of proxies, then the above SPI will not be sufficient. So i thought about something like this one: {code:java} /** * A {@link ProxyFactory} is responsible for creating proxies which are castable to * the {@link Class}(es) specified in the {@link ProxyCreationContext#getTypes()} ** Implementations of {@link ProxyFactory} can expect more contextual information * for proxy creation, through custom {@link ProxyCreationContext}. *
* * @see ProxyCreationContext#getTypes() * @author Jaikiran Pai * @version $Revision: $ */ public interface ProxyFactory { /** * Creates and returns a proxy which is castable to the {@link Class}(es) specified in the passed *proxyCreationContext
. * * @param proxyCreationContext Contextual information for creating of proxies * @return Returns a proxy which is castable to the {@link Class}(es) specified in the passedproxyCreationContext
* @see ProxyCreationContext#getTypes() */ Object createProxy(T proxyCreationContext); } {code} {code:java} /** * {@link ProxyCreationContext} holds the contextual information required for * creation of a proxy. ** Minimally, during the proxy creation, a {@link ProxyFactory} needs to know the * {@link Class}(es) to which the resultant proxy is castable to. The {@link #getTypes()} * returns this information. *
* @see JavaReflectProxyCreationContext * @author Jaikiran Pai * @version $Revision: $ */ public interface ProxyCreationContext { /** * Returns the {@link Class}(es) to which the proxy created by the {@link ProxyFactory} * should be castable to. * @return */ Class[] getTypes(); } {code} So we have a ProxyFactory which has a createProxy method which accepts a ProxyCreationContext. A minimal ProxyCreationContext provides only the getTypes() (which is the Class types to which the resultant proxy should be castable). (I think we might need a getClassLoader() too, but that can be discussed later). Now, a j.l.r based proxy factory will also need a InvocationHandler for associating it with the proxy it creates. So we could have a: {code:java} /** * A {@link JavaReflectProxyCreationContext} in addition to {@link #getTypes()} * also provides a {@link InvocationHandler} as a contextual information for * proxy creation. The {@link InvocationHandler} is then associated with the * proxy created by a call to {@link JavaReflectProxyFactory#createProxy(JavaReflectProxyCreationContext)} * * @author Jaikiran Pai * @version $Revision: $ */ public interface JavaReflectProxyCreationContext extends ProxyCreationContext { /** * Returns a {@link InvocationHandler} which will be associated to the * proxy created by a call to {@link JavassistProxyFactory#createProxy(JavassistProxyCreationContext)} * @return */ InvocationHandler getInvocationHandler(); } {code} and then the j.l.r proxy factory implementation would be : {code:java} public class JavaReflectProxyFactory< T extends JavaReflectProxyCreationContext> implements ProxyFactory { /** * @see org.jboss.ejb3.proxy.spi.ProxyFactory#createProxy(org.jboss.ejb3.proxy.spi.ProxyCreationContext) */ @Override public Object createProxy(T proxyCreationContext) { Proxy.newProxyInstance(loader, proxyCreationContext.getTypes(), proxyCreationContext.getInvocationHandler()); } } {code} Similarly a Javassist based proxy factory (co-incidentally) expects a InvocationHandler for proxy creation. So it too would have a: {code:java} /** * A {@link JavassistProxyCreationContext}, in addition to {@link #getTypes()} * also provides a {@link InvocationHandler} as a contextual information for * proxy creation. The {@link InvocationHandler} is then associated with the * proxy created by a call to {@link JavassistProxyFactory#createProxy(JavassistProxyCreationContext)} * * @author Jaikiran Pai * @version $Revision: $ */ public interface JavassistProxyCreationContext extends ProxyCreationContext { /** * Returns a {@link InvocationHandler} which will be associated to the * proxy created by a call to {@link JavassistProxyFactory#createProxy(JavassistProxyCreationContext)} * @return */ InvocationHandler getInvocationHandler(); } {code} and the proxy factory implementation would look like: {code:java} public class JavassistProxyFactory implements ProxyFactory { /** * @see org.jboss.ejb3.proxy.spi.ProxyFactory#createProxy(org.jboss.ejb3.proxy.spi.ProxyCreatorContext) */ @Override public Object createProxy(T proxyContext) { // do some javassist logic and create a proxy Object proxy = blah(); // associate with the invocation handler associate(proxy, proxyContext.getInvocationHandler()); return proxy; } } {code} These factories could then be used by the (JNDI binder) clients like this: {code:java} public class NoInterfaceJNDIBinder { bindProxy() { // create proxy ProxyFactory proxyFactory = new JavassistProxyFactory(); JavassistProxyCreationContext context = null; // create a JavassistProxyCreationContext Object proxy = proxyFactory.createProxy(context); // bind to jndi ctx.bind(proxy); } } {code} And the other jndi binder could do: {code:java} public class LocalBusinessViewJNDIBinder { bindProxy() { // create j.l.r based proxy ProxyFactory proxyFactory = new JavaReflectProxyFactory(); JavaReflectProxyCreationContext context = null; // create a JavaReflectProxyCreationContext proxy = proxyFactory.createProxy(context); // bind ctx.bind(proxy); } } {code} Thoughts? By the way, feel free to suggest a different name for the interfaces wherever applicable - i am not good at naming.