Announcement Announcement Module
Collapse
No announcement yet.
Sharing contextual object from ProxyFactory caller to interceptor Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Sharing contextual object from ProxyFactory caller to interceptor

    I am using the ProxyFactory to create a proxy with a list of method interceptors. One of these interceptors needs to access an object (in its invoke() method) that exists in the context of the caller of the ProxyFactory. How can I make it available to this interceptor ?

    The cleanest way, in my opinion, would be to set this object in the method invocation object itself (by using the userAttributes map of ReflectiveMethodInvocation), but I do not have access to that object while it is being created.

    There are other directions for doing this, but I don't like them :
    • the client of ProxyFactory sets this object in a ThreadLocal, and the interceptor gets it back and uses it. This is certainely not beautiful, but the main problem is that I do not have the opportunity to clean the ThreadLocal after invocation (except by telling my users to do it).
    • When the client code iterates over the list of advices (in order to add them to the ProxyFactory), I test if this is my interceptor, and inject the object into it. The problem is that I will set a dependency on this interceptor in the client code, and, -more serious- as this interceptor is a singleton in my application context, I need to clone it before setting the object and adding it as an advice to the ProxyFactory (or set this interceptor as a prototype in my application context).

    Setting it in the method invocation would need the following rewrite of some Spring classes :

    client code:
    Code:
    ProxyFactory pf = new ProxyFactory();
    pf.setMethodInvocationUserAttribute("my.context", ctx);
    for (Advice a:advicesList) {
      pf.addAdvice(a);
    }
    pf.setTarget(target);
    Object decorated = pf.getProxy();
    The additional method (setMethodInvocationUserAttribute()) is defined on AdvisedSupport, and maps to a userAttributes map; the JdkDynamicAopProxy.invoke() is changed as follows (line 202 in release 2.0.6):

    Code:
    invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    Map userAttributes = this.advised.getMethodInvocationUserAttributes();
    for (String key: userAttributes.keySet()) {
      invocation.setUserAttribute(key, userAttributes.get(key);
    }
    // line 203 follows
    Doing this, an interceptor can simply access this object in its invoke method :
    Code:
    public Object invoke(MethodInvocation mi) throws Throwable {
      Map context = ((ReflectiveMethodInvocation) mi).getUserAttributes();
      // play with it
    }
    Isn't that exactly what this userAttributes Map in ReflectiveMethodInvocation was supposed to be used to ?
    Of course, I do not have the complete map of Spring AOP framework in mind, but it sounds to me that it's the most elegant way.

    To be more precise, the situation is as follows :
    • the client code is an AbstractStatelessSessionBean which goal is to wrap a POJO service with the same business interface as the EJB
    • there are common method interceptors that must wrap each POJO service call that is accessed via EJB; developers create an EJB, and delegate each method call to the proxied POJO service, loaded, decorated and set as an instance variable in onEjbCreate(). The interceptors are configured in an application context
    • one of these interceptor needs to access the EJBContext.

  • #2
    Concerning the invocation attributes you should consider that setting these from outside would pose a potential concurrency problem. A ThreadLocal approach would indeed be more safe (though not more elegant).

    As I understand, your problem is to make known the EJBcontext, right? Perhaps you could use an approach which I developed for propagation of security context information. It involves an additional interface, though.
    If you are interested, have a look here. Sources and documentation are available.

    Regards,
    Andreas

    Comment


    • #3
      Thanks for your reply, Andreas,

      The situation you developped is not exactly what I need. As I understand your situation, the SecurityContext is in a ThreadLocal on the client side, when the client invokes a service, which implementation is remote EJB; the factory bean for ejb extracts it and passes it as an additional method parameter to the actual EJB; the bean instance reads this additional parameter and sets it back in a ThreadLocal on the server side, and eventually invokes the raw method on the POJO service. Am I correct ?

      However, my situation is different : the EJBContext is only (of course) on the server side, and is an instance variable of the session bean (stored when setSessionContext() callback is invoked by the server). The EJB delegates all calls to a POJO service with the same interface, but the service is actually wrapped in a proxy with predefined interceptors. The POJO service is not aware of the EJBContext, and will not receive it in any method parameter. The goal of sharing the EJBContext is to use it in an interceptor only.

      I need to make this EJBContext available to an interceptor's invoke() method, without storing it as an instance variable of the interceptor (due to the interceptor being a singleton bean shared by many EJBs). This can only be done either by receiving it in the method's parameter, or discovering it by invoking a static getter method on a class, which delegates to a ThreadLocal.

      The ThreadLocal solution is problematic : the service is loaded and proxied only once, and this is performed in onEjbCreate(); once initialized, the proxied service is stored as an instance variable in the ejb instance. When a user method is invoked on the EJB, it just delegates the call to the service instance variable. The ThreadLocal approach requires that every ejb method being implemented this way :
      Code:
      public Result someMethod(Param p) {
        EjbContextHolder.setCtx(getSessionContext());
        try {
          return this.service.someMethod(p);
        } finally {
          EjbContextHolder.resetCtx();
        }
      }
      Notes:
      • EjbContextHolder stores a static ThreadLocal holding the EJBContext
      • This can be achieved in a cleaner template-like fashion, but still noisy.

      The other way of doing this would be to recreate the proxy for each call : in the wrap() method (as described in the inital post), I can also put the above code, but only the first line: the user still has to clean the ThreadLocal before leaving the method.

      The interceptor can then be implemented as follows :
      Code:
      public Object invoke(MethodInvocation mi) throws Throwable {
        EJBContext ctx = EjbContextHolder.getCtx();
        // play with it
      }
      I don't like this solution for many reasons :
      • As a Spring developer, I prefer receiving an object as looking for it.
      • Any EJB method has to add this ugly piece of code around the service invocation. Further, as I have to inform the developer of putting this code around each invocation, why not directly inform them to put the interceptor business instead, wrapped in a clean template ? Why using AOP, then ?

      The proposed solution does not (as far as I can see) have any concurrency threat, as the ProxyFactory is a local variable, instanciated in onEjbCreate() (it does not hold any reference to an outside (shared) object). The Spring InvocationHandler that's used to create the Proxy is the JdkDynamicAopProxy, which holds a reference to that ProxyFactory (in the variable called advised - this is Spring's stuff). The invocation handler is then referenced by the created proxy (this is Sun's stuff), which becomes my wrapped service, which is in turn stored as an instance variable of the EJB. As the EJBContext is initialized exactly once by EJB instance (when setSessionContext() is invoked), I can be sure that the EJBContext referenced in the UserAttribute of the ProxyFactory (in the proposed ideal case) will not vary over ejb instance use.
      When a client invokes a user method on the ejb, the EJBContext that is (ideally) stored in the ProxyFactory in one of its UserAttribute (remember, the ProxyFactory is referenced indirecty by the InvocationHandler and therefore the wrapped service) is the same (==) as the one that comes from an hypothetic call to getSessionContext() from within that EJB user method.
      The call is then delegated to the proxied service, which in turn invokes the invoke() method of the JdkDynamicProxy (the InvocationHandler); the later creates a MethodInvocation (ideally) transferring all the user attributes that are contained in its config (remember, the advised instance variable, actually the ProxyFactory) - the EJBContext being one of these attributes -, and eventually invokes proceed() on it.
      Any configured interceptor can cast the MethodInvocation, that's received as a parameter to its own invoke() method, to a ReflectiveMethodInvocation, and read any UserAttribute, typically the EJBContext.

      That's it. Sounds simple, isn't it ? :-) (a diagram would maybe help). Or did I miss something ?

      Comment


      • #4
        Originally posted by gpitteloud View Post
        The situation you developped is not exactly what I need. As I understand your situation, the SecurityContext is in a ThreadLocal on the client side, when the client invokes a service, which implementation is remote EJB; the factory bean for ejb extracts it and passes it as an additional method parameter to the actual EJB; the bean instance reads this additional parameter and sets it back in a ThreadLocal on the server side, and eventually invokes the raw method on the POJO service. Am I correct ?
        Yes, your description correct. However, I thought about reusing the idea of using some kind of extended interface. You could create an additional interface which takes the EJBContext as extra parameter in every method. That would be the interface to be used in your EJB and you could pass that in on every method delegation. Your interceptor would so be able to access that additional parameter. In the application context you would also configure an ArgumentExtractingProxyFactoryBean to delegate to the real target bean. In that case you would need a ArgumentHandler which does nothing.

        Originally posted by gpitteloud View Post
        The proposed solution does not (as far as I can see) have any concurrency threat, as the ProxyFactory is a local variable, instanciated in onEjbCreate() (it does not hold any reference to an outside (shared) object).
        In your scenario it should work. However, in the general case the concurrence issue exists. That is why I think your proposed enhancement which would allow access to the invocation attributes will not be integrated into the core.

        One other thing just comes into my mind: You could define a factory (i.e. a GoF Factory, no FactoryBean) that could look like this:

        Code:
        public class FooFactory {
          public Foo createFoo(EJBContext ctx) {...}
        }
        That factory could be wired in the context to refer to a "Foo" (which I here assume as target bean). It could create a dynamic proxy holding the EJBContext and wrap that ThreadLocal behavior on each invocation. That way you would have one more indirection in your EJB, but the delegation would be clean.
        Btw I suggest using a generic class for the factories.

        Regards,
        Andreas

        Comment


        • #5
          In your scenario it should work. However, in the general case the concurrence issue exists.
          I am sorry to insist on it, but I do not see the concurrency issue. It should be sufficent to say that the attributes map elements are transferred as is in the method invocation. Then, if someone changes the contents of one of them from somewhere else, invocations will contain the reference to the modified attribute. This is how references are used for.
          As the ProxyFactory itself is being referenced by the InvocationHandler that is used by the proxied object, if my proposal would lead to a concurrency issue, it means that this issue already exists : the ProxyFactory is set as an instance variable of JdkDynamicAopProxy when the proxy is instanciated, and used some times later to instanciate and configure a MethodInvocation when the proxy is used (when a method is invoked on it).

          Can you maybe explain a scenario where this leads to an issue ?

          Further, I do not understand your factory approach. Can you maybe write a piece of (pseudo-)code so that I can see where shall I put what : application context, onEjbCreate(), an ejb-method, the service itself.
          In order to talk the same language, suppose the session bean is of type ServiceEjbBean, and the raw service is simply a Service, configured in an application context (with implementation class ServiceImpl and name "service"). The ejb has an instance variable of type Service (named "service"), that references the "service" Spring bean. Service and ServiceEjbBean both have a method named "m", and the ejb delegates the call to m to service. Where do I use the proposed factory, how and where do I configure it and what is its implementation ?

          Comment


          • #6
            Originally posted by gpitteloud View Post
            It should be sufficent to say that the attributes map elements are transferred as is in the method invocation. Then, if someone changes the contents of one of them from somewhere else, invocations will contain the reference to the modified attribute.
            Yes indeed. But if you set attributes with the intention that these _are_ the same during the following invocation(s), that might prove wrong if someone else does change them in between. After all, setting the attributes and executing an operation are distinct operations.

            Originally posted by gpitteloud View Post
            Further, I do not understand your factory approach. Can you maybe write a piece of (pseudo-)code so that I can see where shall I put what : application context, onEjbCreate(), an ejb-method, the service itself.
            In order to talk the same language, suppose the session bean is of type ServiceEjbBean, and the raw service is simply a Service, configured in an application context (with implementation class ServiceImpl and name "service"). The ejb has an instance variable of type Service (named "service"), that references the "service" Spring bean. Service and ServiceEjbBean both have a method named "m", and the ejb delegates the call to m to service. Where do I use the proposed factory, how and where do I configure it and what is its implementation ?
            I'll try:

            Code:
            public class ServiceFactory {
              private Service target;
            
              public void setTarget(Service service) {
                this.service = service;
              }
            
              public Service createService(EJBContext ctx) {
                 // here we create and return a dynamic proxy (e.g. a JDK reflection proxy)
                 // which implements Service and delegates all invocations to the 
                 // according target methods. It has a private field holding the EJBContext.
                 // Before each delegating invocation it places the context in a well known
                 // ThreadLocal and clears it afterwards.
                 // -> This method creates a new proxy, but delegates to the same target
              }
            }
            Code:
            public class ServiceEjbBean {
              private Service service;
            
            
              public void onEjbCreate {
                ServiceFactory serviceFactory = (ServiceFactory) getBeanFactory().getBean("serviceFactory");
              
                this.service = serviceFactory.createService(getContext());
              }
            
              public void m() {
                this.service.m();
              }
            }
            Code:
              <bean id="service" class="ServiceImpl">
              ...
              </bean>
            
              <bean id="serviceFactory" class="ServiceFactory">
                <property name="target" ref="service"/>
              </bean>
            I hope this clarifies my proposal. I omitted the proxy creation, but I hope you get the idea here. If not, I can elaborate a bit further here.

            Regards,
            Andreas

            Comment


            • #7
              To complete the picture: The interceptor does only need to access the well known ThreadLocal location to get the EJBContext.

              The ThreadLocal handling itself is, as you described before. If you do it on each invocation it is inelegant, but if it's hidden in infrastructure code it is much better.

              Comment


              • #8
                Thanks for your description.
                I will however not use it directly, as there are already of lot of ejbs running around, that load the POJO service from the bean factory, proxy it, and delegate the calls to the proxied service. I need a solution where the existing user EJBs do not have to be modified.

                But if you set attributes with the intention that these _are_ the same during the following invocation(s), that might prove wrong if someone else does change them in between. After all, setting the attributes and executing an operation are distinct operations.
                How about considering these user attributes as configuration properties, i.e. properties that are set once when the object is created, and should not be changed afterwards (they can for example be freezed when ProxyCreatorSupport.active is true)? By the way, there are many Spring objects which behaviour follows this rule : dependency-injected references should usually not be changed once the object is ready.
                In the ProxyFactory, this happens if you create a proxy for a specified TargetSource using getProxy(), then someone else changes the targetSource on your ProxyFactory instance; when you eventually invoke a method on the proxy, you will be surprised to see that the behaviour is not what you expected, due to the targetSource change.


                My first idea was then to use modified versions of the Spring proxy support classes (ProxyFactory, JdkDynamicAopProxy, etc.) to support the setting of userAttributes in the ProxyFactory (or its parent AdvisedSupport); these user attributes would then be used when the invocation handler creates the MethodInvocation.
                However, I will not do it as it implies a complete rewriting of at least 6 classes (copy-paste for 99.9%), as some are package-protected, or have package-protected methods, and instanciation of critical objects is not delegated to configurable factories.

                My final solution will be to programmatically inject the EJBContext in my interceptor (with the support of some kind of EJBContextAwareInterceptor), and reduce the scope of that interceptor to protoype, so that each EJB will get its own instance.

                Comment

                Working...
                X