5 Replies Latest reply on Feb 15, 2010 1:11 PM by jaikiran

    Proxy SPI

    jaikiran
      Based 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 passed proxyCreationContext     * @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.
        • 1. Re: Proxy SPI
          jaikiran

          Let me edit my earlier post (hopefully in the next few minutes). As usual i can't it get it right with this editor.

          • 2. Re: Proxy SPI
            jaikiran

            jaikiran wrote:

             

            Let me edit my earlier post (hopefully in the next few minutes). As usual i can't it get it right with this editor.

            Should be fixed now. Looks like it's my lucky day - took just 2-3 edits to fix it

            • 3. Re: Proxy SPI
              jaikiran

              jaikiran wrote:

               

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

               

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

               

              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.

              After discussing this with Carlo over IRC, i realize that expecting InvocationHandler isn't a bad deal after all. Infact the above SPI looks much simpler and better compared to what i came up with, in the rest of my previous post.
              • 4. Re: Proxy SPI
                dmlloyd
                For Javassist proxies, won't you need to specify a base class as well?  You could, in fact, have a single factory which returns a java.lang.reflect.Proxy if the base class is Object or Proxy, or a javassist proxy otherwise, could you not?
                • 5. Re: Proxy SPI
                  jaikiran

                  david.lloyd@jboss.com wrote:

                  For Javassist proxies, won't you need to specify a base class as well? 

                  Yes. The proposed SPI takes the "interfaces" param, which actually isn't really interfaces. It's the array of types to which the resultant proxy can be cast to. So that would accomodate the base class.

                   

                  david.lloyd@jboss.com wrote:

                   

                  You could, in fact, have a single factory which returns a java.lang.reflect.Proxy if the base class is Object or Proxy, or a javassist proxy otherwise, could you not?

                   

                  That could be done too. But any new proxyfactory implementations (cglib?) would then have to be accomodated into that single implementation.