Announcement Announcement Module
No announcement yet.
Help with putting transactions around webwork actions Page Title Module
Move Remove Collapse
Conversation Detail Module
  • Filter
  • Time
  • Show
Clear All
new posts

  • Help with putting transactions around webwork actions

    Tried proxying the actions using TransactionProxyBean but this apparently creates singletons only and webwork actions cannot be singletons. Where do i go from here? has anyone successfully managed to do this. Is it perhaps better to create an Interceptor and proxy this?


  • #2
    are you sure it's your web controllers that need to be transactional? Normally it would be your business objects whose methods are called from within your controllers (actions).


    • #3
      Correction, the actions are not webworks's but xwork's. This is the command framework we use for our business methods and has nothing to do with the web per se ( of course webwork uses them to produce our views )

      We like xwork immensely, with chains, interceptors, result types etc, it makes wiring our app up so easy its actually fun again. with spring providing resource beans, transactions, hibernate templates etc it is a powerful combination. I will never work on a struts project ever again, it would be too painful after this

      If only I could get spring's transaction proxy factory to not use singletons


      • #4
        this thread in the AOP forum may explain it:

        Last edited by robyn; May 14th, 2006, 11:18 AM.


        • #5
          Just use ProxyFactoryBean yourself, it's not actually that much more verbose, and you can still use parent/child definitions to save some of the typing.

          The least verbose form when using the ProxyFactoryBean with a prototype is to just put the name of the prototype bean as the last name in the 'interceptorNames' list. The proxy factory will then just wrap it with a PrototypeTargetSource, without you having to do it.

          Make sure you set the 'singleton' _property_ of the proxy factory bean to false, and the 'singleton' XML _attribute_ of the actual target bean to false.


          • #6
            Ok, thanks, I'lll give a go

            first rate support guys!


            • #7
              I'm having some trouble with proxied prototypes. First of all I do have it working correctly. I'm using this for xwork actions and an example of what I have is,

                  <bean id="_action" class="com.opensymphony.xwork.ActionSupport" singleton="false"/>  
                  <bean id="_actionProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
                      <property name="proxyTargetClass">
                      <property name="singleton">
                      <property name="interceptorNames">
              This works fine for the actual creation of proxied, prototype beans. However, I'm having a problem with this and Acegi. I've dug in a bit and Acegi's FilterToBeanProxy class uses the getBeansOfType(class, boolean, boolean) function. In Acegi's case the third parameter is true so FactoryBeans are included in the search. getBeansOfType basically iterates through all instances of FactoryBean calling getObjectType to see if it matches the class requested. The problem is getObjectType never calls refreshTarget so the call in getObjectType to getTargetSource().getTargetClass() throws a NullPointer.

              Am I missing something here? Colin, you mention that the ProxyFactory automatically creates a PrototypeTargetSource but it looks to me like it's actually using a SingletonTargetSource that is just recreated by refreshTarget every time getObject is called.

              Sorry for the long post, I'm just reaching the end of my wits. Thanks!


              • #8
                I had prototypes on the brain, so in my previous message I typed PrototypeTargetSource instead of SingletonTargetSource. What I meant there was that a target source was created automatically for you, instead of needing to be created.

                I do consider this a bug, to the extent that if you yourself feed the proxy factory a PrototypeTargetSource, the getObjectType() will work fine. In the case of just feeding the name as the last interceptor, what the code should do is itself create a PrototypeTargetSource from the beginning, or esle do it on demand.

                I've filed a bug about this:

                In the meantime, what should work fine for you is to manually create a PrototypeTargetSource yourself, and feed it in as the targetseource property (don't forget to remove the bean name from the end of the list of interceptors). There's an example of configuring using this mechanism in this older thread:

                Alternately, you could also wrap with BeanNameAutoProxyCreator, as described in the manual, which will work fine for prototypes.
                Last edited by robyn; May 14th, 2006, 12:06 PM.


                • #9
                  Colin, thank you for such a thorough reply. I successfully worked around my previously problem, but I would like to discuss this issue a bit further.

                  I read your bug report and your suggestions makes sense, but I also don't think PrototypeTargetSource is the correct answer for the behavior we are looking for. PrototypeTargetSource creates a new object on every method call. This isn't the desired functionality for a command pattern based framework. I'm pretty sure what this usage requires is the lifecycle provided by the current implementation, a new SingletonTargetSource for every context.getBean request, but for the getObjectType to defer to BeanFactory.getBeanDefinition(beanName).getBeanCla ss().

                  What are you thoughts on this?


                  • #10
                    You're right. I wasn't thinking too straight when I filed the bug report. There's no way you'd want to use PrototypeTargetSource. I've updated the bug report. I think Juergen is going to fix this for 1.1.2. I can also do it too if he doesn't have time...


                    • #11

                      See Juergen's comment on the bug entry. He does have a point. getObjectType should never through a NPE even with a prototype coming in as the last name on the interceptors list, as the targetsource will default to the specail empty target source instance, which will just return null for the type. Now this means you should get a null result from the getObjectType, but that's legal.

                      Can you try to confirm where and how you're getting the NPE? Perhaps the easiest thing to do is to do is put a breakpoint on the code, and attach to the servlet engine. I think things are not what they seem.

                      Now, there's the question of whether the code should still try to do more to return a valid object type in this case (instead of null), which it could, and I think it should...


                      • #12
                        I think Juergen is misunderstanding where the NPE is occurring. The NPE is not occurring on the getTargetSource().getTargetClass() call, but within getTargetClass for SingletonTargetSource. When createAdvisorChain is a called in setBeanFactory the SingletonTargetSource that is created has an underlying null target. So when SingletonTargetSource.getTargetClass is called it executes target.getClass() and NPE. At least I'm pretty sure all of that is true.


                        • #13
                          No, that's impossible. If you look at createAdvisorChain as called form setBeanFactory, all it does when the last name is a prototype name is set the targetName. That it's. Then next in setBeanFactory, only if the proxy is a singleton does freshTargetSource() (which is the only place that creates the SingletonTargetSource) get called at that point.

                          I sitll think have a bad config of some sort, and all is not quite as you are describing it. I was trying to figure out if perhaps you forgetting to set the proxy itself to singleton (the property, not the attribute), but all that would do would create a SingletonTargetSource from the get-go. Not what you want, but it would be a completely valid targetsource, and would not produce the NPE you describe, so that's no it...

                          What version of Spring are you using btw?


                          • #14
                            Colin, thank you for all your help with this. It is far more likely you are correct, but here is the process as I understand it (with Spring 1.1). Within createAdvisorChain the following code is executed (pasted verbatim)

                            // add a named interceptor
                            Object advice = null;
                            // avoid unnecessary creation of prototype bean just for advisor chain initialization
                            if &#40;isSingleton&#40;&#41; || this.beanFactory.isSingleton&#40;this.interceptorNames&#91;i&#93;&#41;&#41; &#123;
                                advice = this.beanFactory.getBean&#40;this.interceptorNames&#91;i&#93;&#41;;
                            addAdvisorOnChainCreation&#40;advice, this.interceptorNames&#91;i&#93;&#41;;
                            Then within addAdvisorOnChainCreation namedBeanToAdvisorOrTargetSource(next) is called. The next variable is the advice parameter which is null since the ProxyFactoryBean property "singleton" is set to false and the interceptor is the xwork action with the singleton attribute set to false. Within namedBeanToAdvisorOrTargetSource null is again passed in. The following code is executed

                            try &#123;
                            	Advisor adv = this.advisorAdapterRegistry.wrap&#40;next&#41;;
                            	return adv;
                            catch &#40;UnknownAdviceTypeException ex&#41; &#123;
                            	// Treat it as a TargetSource
                            	if &#40;next instanceof TargetSource&#41; &#123;
                            		return &#40;TargetSource&#41; next;
                            	else &#123;
                            		// It's not a pointcut or interceptor.
                            		// It's a bean that needs a TargetSource around it.
                            		return new SingletonTargetSource&#40;next&#41;;
                            Since null cannot be wrapped and is not an instance of TargetSource a SingletonTargetSource is created with null as the target. Also, I have confirmed within the debugger that this is what is happening. I am going to create a standalone configuration file that I can post in it's entirety. Thanks again. Mike.


                            • #15
                              I just took a look at the CVS HEAD code and this is handled very differently. It also looks like it solves this problem. Instead of creating a SingletonTargetSource as a result of setBeanFactory it creates a PrototypePlaceholderAdvisor and places that on the advice chain. This way the targetSource is still EMPTY_TARGET_SOURCE and getTargetSource will return null. Since this code has changed so much let me try upgrading before we waste anymore time thinking about this. Mike.