Spring and Webwork2 Integration

Discussions

News: Spring and Webwork2 Integration

  1. Spring and Webwork2 Integration (15 messages)

    WebWork and Spring both play nicely with others. There are a couple of ways to integrate between the two of them, and Christian Parker writes about the various options. Specifically he looks at the multiple ways in which you can use spring-managed beans from webwork actions.

    So now you can have a WebWork action like:
    public class HelloAction extends ActionSupport {
        private Object actionDAO;

        public void setActionDAO(Object dao) {
            ...
        }
    }
    Read Christian Parker in Spring Webwork2 Integration

    Threaded Messages (15)

  2. Better way[ Go to top ]

    Actually, there is a better way to accomplish the same task. As part of the xwork-optional package at xwork.dev.java.net, you can use the SpringObjectFactory to instantiate your Webwork actions as Spring beans. You can view an example at http://wiki.opensymphony.com/display/WW/Spring. This method has a couple of benefits over the external reference method. One benefit is your xwork.xml will not have any dependency on Spring in its configuration. Also, your Action classes will be Spring beans so they can take advantage of everything Spring has to offer.
  3. Better way[ Go to top ]

    I just recently adding Spring support for webwork actions in a project I'm working on and am quite happy with the external-ref approach. I like it because it keeps Spring separate from webwork, and is pretty easy to understand. I suppose the objectfactory approach has its advantages but the one major disadvantage I can see is that you automatically double the amount of configuration code you need for an action. Not only do you have to configure the action with all of its results in xwork.xml, you also have to configure the action as a bean in Spring. While this may offer certain advantages if you're using other aspects of Spring, I find the extra configuration to be more of a burden if all you want is dependency injection.

    I'd also be curious to see what the difference is in overhead with spring as opposed to xwork instantiating and wiring actions and interceptors. I don't know why one would be faster or slower than the other, but it would be interesting. Though I do have some concern with using Spring to handle interceptors, since they're so intrinsic to xwork; I'm guessing that there may be certain optimizations and other things going on that Spring might not be geared towards. It strikes me as similar to using Spring but substituting something else for dependency injection, since interceptors are so core to webwork's functionality.
  4. Better way[ Go to top ]

    I would disagree that the ObjectFactory approach doubles your configuration code. It's more accurate to say that it splits it up between the applicatonContext.xml and xwork.xml. The external refs get moved to the Spring xml, and you can refer to your Spring actions with aliases in xwork.xml. The main reason I switched over from using external-refs to the ObjectFactory is because it allows me to wrap my Xwork actions in Spring-driven transactions, and BeanNameAutoProxyCreator makes it a piece of cake to add this and other functionality as well, such as Acegi security. But to each his own. Two config files isn't so bad after you've spent years in deployment descriptor hell.
  5. Better way[ Go to top ]

    Interesting take on the ObjectFactory approach. I was against it for the same reasons that Drew mentioned above about having to touch two config files when adding an action. I hadn't considered actually wrapping the action in a spring transaction though. Interesting. I suppose if your are calling your DAO multiple times within one action then this is necessary.

    Either way I should have included the ObjectFactory approach in the article. Thanks for pointing it out!

    Ditto on the beauty of only dealing with xwork.xml and applicationContext.xml v.s. 5 files for an EJB! ;)

    CP
  6. Better way[ Go to top ]

    Interesting take on the ObjectFactory approach. I was against it for the same reasons that Drew mentioned above about having to touch two config files when adding an action. I hadn't considered actually wrapping the action in a spring transaction though. Interesting. I suppose if your are calling your DAO multiple times within one action then this is necessary.Either way I should have included the ObjectFactory approach in the article. Thanks for pointing it out!Ditto on the beauty of only dealing with xwork.xml and applicationContext.xml v.s. 5 files for an EJB! ;)CP
    One reason I chose the Action as my transaction boundary is so I can access and/or filter lazy Hibernate collections before the transaction closes the Session. I also have several Actions that use multiple DAO's.

    Plus it allows me to add Acegi security interception at the Action level rather than just the DAO level.

    YMMV.

    Jason
  7. question about your setup.[ Go to top ]

    Have you done all of the following -->

    -used BeanNameAutoProxyCreator
    -proxyied by class
    -mapping the actions to a method (say "save") rather than the interface "execute"?

    Did it work???

    Matt
  8. question about your setup.[ Go to top ]

    Have you done all of the following -->-used BeanNameAutoProxyCreator-proxyied by class-mapping the actions to a method (say "save") rather than the interface "execute"?Did it work???Matt
    Matt. Yes, this is the method I use. However, there is a bug in Spring 1.0.2 (fixed in 1.1) that prevents this from working correctly. I had to build Spring from CVS Head in order to use it. The recently released 1.1 RC1 should work.

    Jason

    This is the config I use:
    <!-- xwork.xml -->
    <action name="deleteproduct" class="crudProductAction" method="delete">
        <result name="success">/content/productview.vm</result>
    </action>

    <!-- applicationContext.xml -->
    <bean id="crudProductAction" class="com.mycompany.myapp.CreateEditDeleteProductAction" singleton="false">
        <property name="productDao">
            <ref local="productDao"/>
        </property>
    </bean>

    <bean id="actionTxAttributes" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
        <property name="properties">
            <props>
                <prop key="delete">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
    <bean id="actionTxInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager">
            <ref bean="transactionManager"/>
        </property>
        <property name="transactionAttributeSource">
            <ref bean="actionTxAttributes"/>
        </property>
    </bean>
    <bean id="actionProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="proxyTargetClass">
            <value>true</value>
        </property>
        <property name="interceptorNames">
            <list>
                <idref local="actionTxInterceptor"/>
            </list>
        </property>
        <property name="beanNames">
            <list>
                <idref local="crudProductAction"/>
            </list>
        </property>
    </bean>
  9. hmm[ Go to top ]

    I will have to give it a try again. Just need to get approval for 1.1
  10. Better way[ Go to top ]

    As I said, if you're using something in Spring other than DI (like transactions), then I guess the objectfactory approach would make sense. I'm still leery of editing the same thing in 2 different files, though. Also, if you're spanning a transaction over the course of your action, such as several DAO calls, wouldn't you rather put that behind some sort of facade? That way it can be used again by something not webwork related, if necessary. I'm a bit loathe to allow my web layer to control much more than the process of translating requests to and from my business layer, so I would prefer to leave the transactions in Spring, behind Spring-managed facades, and link them up explicitly to the webwork actions. To each his own, however.

    I agree with the other poster about the idea of having Spring do its magic automatically, the way the ComponentInterceptor works. Though I'm not quite sure how that would be possible or efficient, since the component interceptor uses interfaces to define injection points (Type 1 IoC, if I'm not mistaken!).

    Drew
  11. it should be pretty simple[ Go to top ]

    re: Though I'm not quite sure how that would be possible or efficient, since the component interceptor uses interfaces to define injection points (Type 1 IoC, if I'm not mistaken!)


    Make it work the same way it works not, the enabler would be the same.
    The "class" however would be a bean id from spring.


    If the object implements the enabler all is good.
    <component>
    <scope>application</scope>
    <!-- just like how class works not for actions as spring integration -->
    <class>persistenceManager</class>
    <enabler>com.diamondip.ipc.persistence.hibernate.SessionFactoryAware</enabler>
    </component>

    Though if your were going to make this change. "external ref" or "alias" might be more clear than "class".

    Matt
  12. typo[ Go to top ]

    <!-- just like how class works for actions using spring integration -->
  13. Better way[ Go to top ]

    As I said, if you're using something in Spring other than DI (like transactions), then I guess the objectfactory approach would make sense. I'm still leery of editing the same thing in 2 different files, though. Also, if you're spanning a transaction over the course of your action, such as several DAO calls, wouldn't you rather put that behind some sort of facade? That way it can be used again by something not webwork related, if necessary. I'm a bit loathe to allow my web layer to control much more than the process of translating requests to and from my business layer, so I would prefer to leave the transactions in Spring, behind Spring-managed facades, and link them up explicitly to the webwork actions.
    Drew.

    I guess it depends on how you view your action objects. My view is that XWork actions make up the business layer, implementing business logic and calling DAO's. The actions are completely independent of any Web API's, as I have SOAP and XML/HTTP services that also invoke XWork actions/commands by way of XWork's ActionFactory (Btw, that's also how I unit test them). My web/presentation layer consists strictly of velocity templates that the ActionResult forwards to.

    Anyhow, that's how I've chosen to implement Xwork. YMMV.

    Jason
  14. Better way[ Go to top ]

    Mark me noisy... but I see that there is *alot* of Spring news on this site.
  15. my usage[ Go to top ]

    It would be nicer if there was a spring enabled version of xwork's "component" interceptor.

    That way, you don't have to explicitly say "external-ref", except when you want to be exact. <external-ref name="actionDAO">MainDAO</external-ref>

    I currently name my actions in spring.
    In the form of --->

    <bean id="namingPolicyAction" class="com.diamondip.ipcontrol.actions.NamingPolicyAction" singleton="false" autowire="byType"/>
    <bean id="productAction" class="com.diamondip.ipcontrol.actions.ProductAction" singleton="false" autowire="byType"/>
    <bean id="resourceRecAction" class="com.diamondip.ipcontrol.actions.ResourceRecAction" singleton="false" autowire="byType"/>
    <bean id="rootFileAction" class="com.diamondip.ipcontrol.actions.RootFileAction" singleton="false" autowire="byType"/>

    Therefore my actions are wired up by spring...

    However, there are still some problems with this setup -->
    --->Duplication of declaring my actions

    Transactions --> I would like various actions to execute in transactions.
    However, I don't want to define an proxy for every action. I could use the BeanNameAutoProxyCreator, but this fails when
    I don’t' proxy by interface(don't want to write an interface for every action) AND I am mapping this to a method of the action rather than the default "execute()".

    Execute works because, if there is no method mapped xwork just casts the action to the Action interface and everything works very well.

    However, if the action refers to "delete" -->

        public String delete() throws Exception {
         boolean deleted = false;
            for(Iterator i = selectMap.keySet().iterator(); i.hasNext(); ) {
            
             Integer id = (Integer)i.next();
    manager.delete(manager.load(DnsView.class, id));
    deleted = true;
            }
            
            if (deleted) {
             reOrderList();
            }
            
            return SUCCESS;
        }

    Its mapped as follows -->
    <action name="delete" class="dnsViewAction" method="delete">
    <result name="error" type="chain">list</result>
    <result name="success" type="chain">list</result>
    <result name="error" type="chain">list</result>
    </action>


    Problems arise since you can't use the standard reflection api to invoke "delete" on the cglib proxy'd object.

    I gave up on trying to auto define transactions on my actions.
    Instead, I have define <interceptor-ref name="transaction"/> on the actions that need to run in transactions.
    This xwork interceptor is pretty much a no-opp interceptor that just does.

    public String intercept(ActionInvocation i) throws Exception {
    return i.invoke();
    }

    However, I define transaction behavor for it in spring.


    <!--
    This is an Xwork interceptor, runs the intercepted action as a transaction,
    this object gets optionallly added as an xwork interceptor
    -->
    <bean id="myActionTransactionInterceptor" class="com.diamondip.ipcontrol.persistence.TransactionInterceptor" />

    <!-- Transactional proxy for actionTransactionIterceptorImpl -->
    <bean id="actionTransactionInterceptor" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" >
    <property name="target">
    <ref bean="myActionTransactionInterceptor"/>
    </property>
    <property name="transactionAttributes">
    <props>
    <prop key="intercept">PROPAGATION_REQUIRED</prop>
    </props>
    </property>
    </bean>

    This provides an easy way to make my specific actions run in a transaction.
    Delete mapping becomes -->

    <action name="delete" class="dnsViewAction" method="delete">
    <interceptor-ref name="transactionStack"/>
    <result name="error" type="chain">list</result>
    <result name="success" type="chain">list</result>
    <result name="error" type="chain">list</result>
    </action>

    Matt
  16. RE: my usage[ Go to top ]

    It would be nicer if there was a spring enabled version of xwork's "component" interceptor.That way, you don't have to explicitly say "external-ref", except when you want to be exact. <external-ref name="actionDAO">MainDAO</external-ref>
    This would be pretty easy to do... You could have a very simple interceptor which just does this:
    <code>
    ((ConfigurableApplicationContext)appContext).getBeanFactory().autowireBeanProperties(bean,AutowireCapableBeanFactory.AUTOWIRE_BY_NAME,false)
    </code>
    ... you could even parameterize it with whether it should do by type or by name.
    Problems arise since you can't use the standard reflection api to invoke "delete" on the cglib proxy'd object.
    Our code handles this... it looks to see if it's a Proxy and calls the method:
    <code>
                if (action instanceof Proxy) {
                    try {
                        return (String) Proxy.getInvocationHandler(action).invoke(action, method, new Object[0]);
                    } catch (Throwable throwable) {
                        throwable.printStackTrace();
                        throw new Exception("Error invoking on proxy: " + throwable.getMessage());
                    }
                } else {
                    return (String) method.invoke(action, new Object[0]);
                }
    </code>
    I gave up on trying to auto define transactions on my actions. Instead, I have define <interceptor-ref name="transaction"/> on the actions that need to run in transactions.This xwork interceptor is pretty much a no-opp interceptor that just does. public String intercept(ActionInvocation i) throws Exception { return i.invoke();}However, I define transaction behavor for it in spring. <!-- This is an Xwork interceptor, runs the intercepted action as a transaction, this object gets optionallly added as an xwork interceptor --> <bean id="myActionTransactionInterceptor" class="com.diamondip.ipcontrol.persistence.TransactionInterceptor" /> <!-- Transactional proxy for actionTransactionIterceptorImpl --> <bean id="actionTransactionInterceptor" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" > <property name="target"> <ref bean="myActionTransactionInterceptor"/> </property> <property name="transactionAttributes"> <props> <prop key="intercept">PROPAGATION_REQUIRED</prop> </props> </property> </bean> This provides an easy way to make my specific actions run in a transaction.Delete mapping becomes --><action name="delete" class="dnsViewAction" method="delete"> <interceptor-ref name="transactionStack"/> <result name="error" type="chain">list</result> <result name="success" type="chain">list</result> <result name="error" type="chain">list</result></action>Matt
    That's pretty cool!