Announcement Announcement Module
Collapse
No announcement yet.
Transaction proxies and method delegation Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Transaction proxies and method delegation

    I'm using TransactionProxyFactoryBean to wrap my business service objects. Within one of them, I have methods that delegate to others in the same class. However, the methods have different transaction semantics, so I'm assuming I can't call one from the other directly--I have to go through the proxy.

    My question is: How can I get a reference to the proxy given that my business service objects are not Spring-aware, and I'd like to keep it that way? I was hoping I could create a property in the business object for the proxy (using the business object's interface as the property type) and use the Spring container to set the property. Unfortunately, this doesn't work because it creates a circular reference.

    Any ideas?

    Thanks.

  • #2
    BTW- I have seen threads that talk about using AopContext.currentProxy() to get a reference to the proxy. But since my main reason for using declarative transactions is to avoid coupling my business objects to Spring APIs, this approach doesn't solve the problem.

    Thanks in advance.

    Comment


    • #3
      I would recommend to refactor your business code. So you could have one facade handler bean delegating to several sub-handler beans. The methods on the sub-handlers are defined according to the required transactional semantics, the facade-handler works without explicit transaction specification.

      So you can set the transactional proxies of the sub-handler beans as properties in your facade handler bean. You would not be dependent on spring in your code and there would be no cycles.

      Regards,
      Andreas

      Comment


      • #4
        Thanks for the suggestion, but given the interface I'm working with, it seems unnatural to break up these methods into spearate classes.

        I think I have a solution that works, though. I should be able to use a BeanPostProcessor to give any business object a reference to its transaction-aware proxy. First, I define a simple interface that can be implemented by objects that need to reference themselves.

        Code:
        public interface ISelfReferencing {
        
            public void setMe(Object me);
            
        }
        Then, I create and deploy a post-processor that looks for singleton beans produced by a FactoryBean which implement the ISelfReferencing interface. For each one it finds, it calls setMe to pass in the proxy. Here's what my post-processor code looks like. Only postProcessAfterInitialization does something useful.

        Code:
            public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
                if(bean instanceof FactoryBean) {
                    FactoryBean factory = (FactoryBean)bean;
                    if(factory.isSingleton()) {
                        Object managedObject;
                        try {
                            managedObject = factory.getObject();    
                        } catch (Exception e) {
                            throw new FatalBeanException("Cannot post-process bean.", e);
                        }
                        if(managedObject instanceof ISelfReferencing) {
            	            ISelfReferencing selfRefBean = (ISelfReferencing)managedObject;
            	            selfRefBean.setMe(managedObject);                    
                        }
                    }
                }
                return bean;
            }
        It seems to work. All I have to do is implement ISelfReferencing and then use the proxy in place of this for local calls. The only thing I'm worried about is that I don't know if it's acceptable to call FactoryBean#getObject() in the post-processor method. Is the factory and its contained object guaranteed to be fully-initialized at that point?

        Comment


        • #5
          The facgtory bean will be fully initialized by the time the post-processor is called, so your technique should be fine.

          Comment


          • #6
            Now that I think of it, can't the problem be solved in a more bullet-proof way by just using CGLIB for the proxies? Is there any drawback? (I'm assuming there's a reason Spring doesn't use it by default.)

            Comment


            • #7
              Originally posted by rhasselbaum
              Now that I think of it, can't the problem be solved in a more bullet-proof way by just using CGLIB for the proxies? Is there any drawback? (I'm assuming there's a reason Spring doesn't use it by default.)
              Sure

              The programming against interfaces is preferrable because of design reasons. One major advantage is the decoupling of code. This is not as easy with classes since you have always couplings due to your implementation (which should not be known to clients).
              From a practical point of view: The enhancement of classes with CGLIB is subject to restrictions as are all subclasses: e.g. you cannot override final methods.

              Regards,
              Andreas

              Comment


              • #8
                Actually, I just tried it and it does not work. When I set a breakpoint in the code, I can see from the call stack that the CGLIB proxy intercepts the call when it is made from a different object. But when one method calls another in the same object, the CGLIB proxy does not intercept it. How is this possible if CGLIB is generating a subclass? Doesn't polymorphism still work? (Excuse my ignorance. I know nothing about CGLIB.)

                Here's the top of my call stack. It shows the enhanced class (CatalogBuilderServiceImpl), and at the very top, it shows one method calling another in the same object. Neither method is final.

                Code:
                Thread [main] (Suspended)
                	CatalogBuilderServiceImpl.buildMandatorySnapshot(DbmsInstance) line: 106
                	CatalogBuilderServiceImpl.buildSnapshot(DbmsInstance) line: 53
                	CatalogBuilderServiceImpl$$FastClassByCGLIB$$76899d40.invoke(int, Object, Object[]) line: not available
                	MethodProxy.invoke(Object, Object[]) line: 149
                	Cglib2AopProxy$MethodInvocationImpl.invokeJoinpoint() line: 890
                	Cglib2AopProxy$MethodInvocationImpl(ReflectiveMethodInvocation).proceed() line: 116
                	TransactionInterceptor.invoke(MethodInvocation) line: 56
                	Cglib2AopProxy$MethodInvocationImpl(ReflectiveMethodInvocation).proceed() line: 138
                	Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Object, Method, Object[], MethodProxy) line: 597
                	CatalogBuilderServiceImpl$$EnhancerByCGLIB$$9879438a.buildSnapshot(DbmsInstance) line: not available
                	CatalogBuilderClient.buildUsingInstance(Long) line: 149

                Comment


                • #9
                  How does buildSnapshot() invoke buildMandatorySnapshot()?
                  Do you use the proxy here? If yes, I wonder why it should not work.

                  Regards,
                  Andreas

                  Comment


                  • #10
                    No, I'm making the call through this. That's my point. I was hoping that by using CGLIB, I would not need to have a separate reference to the proxy. Otherwise, CGLIB doesn't buy me anything over dynamic proxies.

                    Comment


                    • #11
                      I see. I must confess that I am not familiar with the code that CGLIB generates. From your observation it seems, that a subclass+delegate strategy is being applied.

                      So a subclass would be generated which performs preprocessing, delegates to the delegate instance of superclass type and performs postprocessing afterwards. This way the implementation of the "original" type will not be touched and the mechanics are consistent with the interface-proxy approach.

                      Conceptually it should look like that (if I surmise the CGLIB approach correctly)
                      Code:
                      class Foo {
                        void bar() {}
                      }
                      
                      class FooProxy extends Foo {
                        Foo foo;
                      
                        void bar() {
                          // preprocessing
                          foo.bar();
                          // postprocessing
                        }
                      }
                      If no one else has a deeper insight here, I fear you have to use your initial approach with setting the proxied instance.

                      Regards,
                      Andreas

                      Comment


                      • #12
                        Your understanding of the way CGLIB is being applied is the same as mine--although I think you meant super.bar() rather than foo.bar(), right? In any case, I should see FooProxy.bar() in the call stack when I call bar() from another method in the Foo base class. I don't see it.

                        Comment


                        • #13
                          Actually I meant foo.bar().
                          I guess that the proxy-type extends the supertype but does not directrly delegate to super.
                          Instead of generating a subtype and initializing this subtype, Spring creates the "real" bean and creates a proxy of a generated subtype additionally.

                          Thence you cannot see the proxy-method when invoking one method from the other. The first method you invoke is the proxy-method. This method eventually delegates to the delegate instance (the foo instance). Once inside the foo.bar() method the proxy is not known. So further calls will be unproxied.

                          Regards,
                          Andreas

                          Comment


                          • #14
                            That's unfortunate. Okay, well, I will go with the ISelfReferencing approach then. Thanks for your help!

                            Comment

                            Working...
                            X