Porting the Sell Item SWF sample to RSF with SWF#
This page documents my efforts to port the Sell Item sample Spring Web Flow (SWF) app to using the RSF/SWF integration.
This conversion took approximately 6 hours to complete (including documenting the process). Breakdown is as follows: approximately 1 hour to make the original app work with Maven2, 1 hour to understand the structure of the sample app, 1 hour for initial structure setup and cleanup, 1 hour to convert the JSPs to html templates with rsf:ids, 1 hour to write the RSF producers, 1 hour to for miscellaneous changes (updates to .xml, .properties).
I would estimate that a real app conversion for an app this size would take only 3 hours since documenting the process and understanding the app would not be required. You could probably save almost another hour if the app was already using maven2.
Feel free to send me (Aaron Zeckoski) questions or comments (or using the mailing list).
Sample Code (SVN): | original code (with maven2 build) | RSF SWF version |
---|
My development environment#
- Java 1.5
- Tomcat 5.5.12
- Maven2
- Eclipse 3.2
- Windows XP (any OS should be fine)
Getting started#
First thing I had to do was get the SWF sample code up and running on my machine.- Went to the Spring Download page and grabbed version 1.0.4 of SWF (this just goes over to sourceforge for the spring-webflow-1.0.4.zip)
- Extracted the package and grabbed the sellitem sample app (spring-webflow-1.0.4\projects\spring-webflow-samples\sellitem) and copied it over to my desktop
- Imported the sample app in eclipse and added a maven2 pom.xml file (also adjusted the .classpath file to all required jars to use M2_REPO variable).
- TIP: Look at the ivy.xml file to get the required dependency list and versions
- TIP: Don't forget to define a M2_REPO classpath variable in eclipse which points to your local maven2 repository
- Built the project using maven2 by running mvn install
- Started up tomcat and accessed the app at: http://localhost:8080/SWFSellItem-orig-1.0/
- Checked in the code for the original version to the CARET SVN
Creating the RSF version#
Initial structure setup and cleanup#
- Branched the original code to create the RSFSWF version.
- Adjusted the maven2 pom.xml to remove unneeded dependencies (webmvc, web, etc.) and added in the rsf-util and spring-aop dependencies.
- Also changed the Spring version to 2.0.4 to make it easier for maven2 to sync and require less specified dependencies in the pom.xml
- Added rsf-swf and rsfutil jars to the eclipse classpath (using the M2_REPO variable)
- Added in an RSF standard content directory under /src/main/webapp
- Added in RSF standard content/templates, content/css, and content/images directories
- Moved *.jsp files to content/templates and renamed all files to .html
- Updated the headers of all .html files to remove taglibs and added in standard RSF headers (also removed meta tag)
- Moved *.jpg to content/images and removed old /src/main/webapp/images directory
- Copied in the rsf logo and fixed the relative path to all images in the templates
- Moved /src/main/webapp/style.css file to content/css
- Fixed the css paths in the headers
- Added /src/main/webapp/WEB-INF/messages/messages_en.properties file for internationalized messages
- Created an rsf package in org.springframework.webflow.samples.sellitem
- Added params and producers packages to the rsf package
Actual real conversion work#
- Removed the /src/main/webapp/WEB-INF/sellitem-servlet-config.xml (it only has webmvc beans which we do not need)
- Updated the /src/main/webapp/WEB-INF/web.xml file to include the RSF standard contexts and servlets
- This basically means completely overwriting what is there except you can keep the classpath:org/springframework/webflow/samples/sellitem/services-config.xml
- Here is a link to the new web.xml file for easy reference
- Create an applicationContext.xml and requestContext.xml file in /src/main/webapp/WEB-INF
- Here are links to each for easy reference: applicationContext.xml, requestContext.xml
- OPTIONAL: added an RSF config set under SpringIDE which includes all spring files
- Went through each .html template and removed the JSP code and replaced it with rsf:ids
- This mostly involved the removal of non-standard html tags like <form:form> and <c:...
- This also involved some moving around of tags to improve the xhtml
- Created producers for each of the html files in the producers package
- This is the best practice way to create RSF view producers
- Create a class named <template>Producer which implements ViewComponentProducer
- Create a public final static String VIEW_ID = "<template>"; which is set to the template without .html
- Make the getViewID method return the VIEW_ID variable
- Made the IndexProducer implement DefaultView so it is the starting page for the app
- Added real code to the IndexProducer to create the links to initiate the various flows
- Create a link to start a flow: UIInternalLink.make(tofill, "sellitem-flow-link", new SWFLaunchViewParams("sellitem-flow"));
- From the template: <a rsf:id="sellitem-flow-link" href="priceAndItemCountForm.html">Sell Item</a>
- Added real code to the PriceAndItemCountFormProducer to connect data to the form and associate the submit with the flow
- Use the RSF convention of SWFBindingBean to proxy data to the flow formObject, this points to the org.springframework.webflow.samples.sellitem.Sale.java object which has fields on it like .price,
For example: UIInput.make(form, "sale-price", "SWFBindingBean.price");- NOTE: Here we are putting data into the sale bean
- Use the RSF convention of SWFEventBean to continue the flow, this ties to the view-state in the flow
(/src/main/webapp/WEB-INF/flows/sellitem-flow.xml) and the related transition which will have an on,
For example: <transition on="submit" to="enterCategory"> for view-state id=enterPriceAndItemCount
- Use the RSF convention of SWFBindingBean to proxy data to the flow formObject, this points to the org.springframework.webflow.samples.sellitem.Sale.java object which has fields on it like .price,
- Added real code to the CategoryFormProducer to connect data to the form and associate the submit with the flow
- Use the RSF convention of SWFBindingBean to proxy data from the flow formObject, this points to the org.springframework.webflow.samples.sellitem.Sale.java object which has fields on it like .itemCount,
For example: UIOutput.make(tofill, "sale-itemCount", "SWFBindingBean.itemCount");- NOTE: Here we are getting data back out of the sale bean, before we were putting it in
- Use the RSF convention of SWFBindingBean to proxy data from the flow formObject, this points to the org.springframework.webflow.samples.sellitem.Sale.java object which has fields on it like .itemCount,
- Added real code to the ShippingDetailsFormProducer to connect data to the form and associate the submit with the flow
- There is some optional rendering on this view and a pulldown which requires adding the options text to
/src/main/webapp/WEB-INF/messages/messages_en.properties - NOTE: This view differs from the others in that it does not perform SWF form setup on entry to the state. As a result, EL references to the model must be via sale.* when rendering fields rather than SWFBindingBean.*.
- There is some optional rendering on this view and a pulldown which requires adding the options text to
- Added real code to the CostOverviewProducer to connect data to the form and associate the submit with the flow
- NOTE: This view differs in that it uses the end flow state. This does not actually impact our conversion but it is worth noting that RSF handles this automatically.
- Added all producer bean definitions to the requestContext.xml file
- Added the validation messages from /src/main/java/org/springframework/webflow/samples/sellitem/SaleValidator.java to the /src/main/webapp/WEB-INF/messages/messages_en.properties
- This is because RSF only allows internationalized messages
- Committed the changed code to the CARET SVN repository RSF samples trunk
Running the sample app#
- Built and deployed the project using maven2 by running mvn install cargo:deploy from the main directory
- Started up tomcat and accessed the app at: http://localhost:8080/SWFSellItem-SWFRSF-1.0/faces/
- Tested the various flows
Differences in app function#
- Hitting the browser back button after reaching the final flow will yield an exception on screen in the original app, this behavior is the same in RSF except that RSF handles the exception and redirects the user back to the Home screen
- In the original app, hitting refresh once you reach the end flow will yield an exception on screen, in RSF the end flow state is preserved and the end flow screen can be refreshed and will display the results of the flow again
Summary of needed changes#
- No flows were changed and the logic for the application was not touched
- The "views" (JSPs) were transformed into RSF views (producers and templates) and various RSF bits were added (contexts)
- A few other RSFisms like internationalized messages and the web.xml had to be updated or added