This article has since been moved to the WebWork 2 wiki.
I recently started to work with WebWork 2, Spring and Hibernate 2 in an effort to utilize some of the more popular open source frameworks and libraries out there. While the Hibernate 2 documentation was quite complete and integration efforts went smoothly, as did getting WebWork 2 set-up, getting Spring integrated with Webwork 2 (or vice-versa actually) turned out to require some tweaking.
I started out using the original WebWork documentation to get Spring to initialize Webwork actions, but it appears that a lot has changed since the days of WebWork 1.x. This will be my attempt to clarify some of those changes and to list the steps necessary to get the two to play nicely. Please comment with clarifications or corrections!
WebWork 1.x (these are assumptions based on the 1.x documentation mentioned above)
It seems that the way you got Spring to initialize WebWork 1.x action classes was to add this line into your webwork.properties file:
webwork.action.factory=webwork.action.factory.SpringActionFactory
so that WebWork would know to use Spring’s view of the world to create actions. The WebWork action classes would then need to be declared in the Spring applicationContext.xml file so that Spring would know directly of the action objects. Upon invocation of an action, WebWork would know to first use the SpringActionFactory to try and create an instance of the requested action which would ask Spring to create the object using its configuration. If there was no Spring definition of that action object, then WebWork would use it’s normal instantiation methods to create that action. Well, things have changed slightly since WebWork 1.x.
WebWork 2 In WebWork 2 (the functionality actually exists in XWork), you specify relationships from action classes to other objects in XWork’s xwork.xml file instead of Spring’s applicationContext.xml file. So if you have an action class that utilizes a DAO, instead of having a bean definition like so in applicationContext.xml:
<bean id="myAction" class="com.ryandaigle.web.actions.MyAction"
singleton="false">
<property name="DAO">
<ref bean="myDAO"/>
</property>
</bean>
<bean id="myDAO" class="com.ryandaigle.persistence.MyDAO"
singleton="true" />
you move the action definition to xwork.xml and keep the DAO definition in applicationContext.xml so that xwork.xml looks like:
<action name="myAction"
class="com.ryandaigle.web.actions.MyAction">
<external-ref name="DAO">myDAO</external-ref>
<result name="success" type="dispatcher">
<param name="location">/success.jsp</param>
</result>
</action>
and applicationContext.xml looks like:
<bean id="myDAO" class="com.ryandaigle.persistence.MyDAO" singleton="true" />
Notice how there is the external-ref element in the action definition that points to an object that Spring is managing. There are several things that need to be in place for the external-ref to work, but I just wanted to give an overview of what has changed before going into the specific steps.
Steps for Configuring Spring/WebWork (XWork) Integration:
- Get the files you need to externally resolve Spring beans. I’ve bundled them all here. They were originally spread between two JIRA issues filed against XWork 1.0 (see references below). This zip includes the source, the class files (so you can just include it in your classpath) and my example configuration files. Either extract the source files into your application, or put the file onto your classpath. (You may want to take the applicationContext.xml and xwork.xml files out, I don’t know if they’ll override your files… They’re just there as an example configuration).
- Now, let’s get your XWork configuration file (xwork.xml) to resolve external references. XWork resolves external references (using the external-ref element) by utilizing an external reference resolver per package. You specify your external reference resolver as an attribute of the package element:
<package name="default" extends="webwork-default" externalReferenceResolver= "com.atlassian.xwork.ext.SpringServletContextReferenceResolver">
This SpringServletContextReferenceResolver class reference is a class not part of the XWork distribution written as an extensions for XWork/Spring that I got from this JIRA issue filed against XWork addressing this Spring integration effort. (I have bundled it with the rest of the necessary files later on down for your convenience). This class will intercept all external-refs and resolve the references using Spring’s context. There is also a SpringApplicationContextReferenceResolver included in the zip file that will allow you to resolve Spring references for applications not executing within the web context. But as this is a WebWork/Spring article, the servlet resolver is what we need to use. -
Now we need to add the XWork reference resolver as part of the interceptor stack you’re using. This will allow any references to be resolved (using the reference resolver you specified in the externalReferenceResolver attribute). This is how I’ve added that interceptor:
<interceptors> <interceptor name="reference-resolver" class= "com.opensymphony.xwork.interceptor.ExternalReferencesInterceptor"/> <interceptor-stack name="myDefaultWebStack"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="reference-resolver"/> </interceptor-stack> </interceptors> <default-interceptor-ref name="myDefaultWebStack"/> -
As I briefly outlined before, you can now reference Spring beans that your action classes need in xwork.xml:
<action name="myAction" class="com.ryandaigle.web.actions.MyAction"> <external-ref name="DAO">myDAO</external-ref> <result name="success" type="dispatcher"> <param name="location">/success.jsp</param> </result> </action>And that’s all we have to do to xwork.xml to let XWork know how to resolve references to Spring’s managed beans. -
Now let’s setup our web environment to properly notify Spring and our external reference resolver of the web context. We do this by adding two context listeners to your application’s web.xml file:
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <listener> <listener-class> com.atlassian.xwork.ext.ResolverSetupServletContextListener </listener-class> </listener>
The first listener is Spring’s that you would need independent of whether or not you were integrating with WebWork 2. The second listener is our external resolver’s that will use the servlet context to retrieve Spring’s application context. This is the link between WebWork and Spring.
At this point, we’ve set up Spring and our XWork reference resolver to work within a web context, and we’ve told XWork how to resolve external references to Spring. We’re done! Fire it up and let me know if there are some steps I’ve missed or assumptions I’ve made that I shouldn’t have.
References
- Here is my bundled source, class and example configuration files (that contains all the needed referenced files below); webwork2-spring.jar
- My searching started with the original WebWork 1.x + Spring documentation and comments on the Wiki; http://wiki.opensymphony.com/comments/Spring+Framework+Integration
- The Wiki pointed me to the two JIRA issues that contained the source files for the reference resolvers:
- http://jira.opensymphony.com/secure/ViewIssue.jspa?key=XW-122; The “SpringExternalResolver.zip” attachment is the one needed for externally resolving Spring objects.
- http://jira.opensymphony.com/secure/ViewIssue.jspa?key=XW-132; The “xwork-springServletImpl.zip” attachment is the one needed for externally resolving Spring objects. It just contains some files missing from the original source.
Credits
Judging by the comments etc… of the JIRA issues filed against XWork, it appears that Ross Mason (of Atlassian?) is the man to thank for the external reference resolver code. And of course we have to thank the people of Spring and WebWork 2 for making this all possible.
Please let me know if I’ve missed anything.
