Porting the Shipping Rate SWF sample to RSF with SWF#
This page documents my efforts to port the Shipping Rate - An Ajax-enabled Spring Web Flow 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 shippingrate app (spring-webflow-1.0.4\projects\spring-webflow-samples\shippingrate) 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/SWFShippingRate-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
- Removed the old jsp directory
- 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
- Added logos to the front page, they were not used in the original app
- Moved /src/main/webapp/style.css file to content/css
- Added the css to the index header
- Adjusted css since body font size was too small to read on high definition screens
- Added /src/main/webapp/WEB-INF/messages/messages_en.properties file for internationalized messages
- Created an rsf package in org.springframework.webflow.samples.shippingrate
- Added params and producers packages to the rsf package
Actual real conversion work#
- 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/shippingrate/domain/services.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
- Removed the /src/main/webapp/WEB-INF/shippingrate-servlet.xml (it mostly has webmvc beans which we do not need)
- Copied over the 3 flow bean definitions from within this file (flowExecutor, flowRegistry, and formAction) to the applicationContext.xml
- Updated the header of the applicationContext to include the flow xmlns xmlns:flow="http://www.springframework.org/schema/webflow-config"
- 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 <c:... and <spring:...
- This also involved some moving around of tags to improve the xhtml
- Also removed hidden input fields which were carrying along the flow id
- 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
- Added all producer bean definitions to the requestContext.xml file
- 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 flow inside a form so we can start it using an ajax call
- Added in special form tag to allow us to get the flow start url
<form rsf:id="rate_flow_link_form" action="selectCustomer.html" /> - Create the form: UIForm form = UIForm.make(tofill, "rate_flow_link_form");
- Create a link to start a flow and put it in a form: form.viewparams = new SWFLaunchViewParams("getRate-flow");
- Added in special form tag to allow us to get the flow start url
- Added real code to the SelectCustomerProducer 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.shippingrate.domain.RateCriteria.java object (defined by the formObjectClass in the formAction bean) which has fields on it like .residential,
For example: UISelect choices = UISelect.make(form, "customer_choices", customerOptions, customerLabels, "SWFBindingBean.residential");- NOTE: Here we are putting data into the rateCriteria bean (defined by the formObjectClass in the formAction 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/getRate-flow.xml) and the related transition which will have an on,
For example: <transition on="submit" to="selectSender"> for view-state id=selectCustomerType
- Use the RSF convention of SWFBindingBean to proxy data to the flow formObject, this points to the org.springframework.webflow.samples.shippingrate.domain.RateCriteria.java object (defined by the formObjectClass in the formAction bean) which has fields on it like .residential,
- Added the injection for the RateService to the remaining producers
- it is used in every view except IndexProducer and SelectCustomerProducer
- Added functional code to the remaining producers (this is basic Java and RSF code so I will not go into detail here)
- This did involve injecting the RateCriteria bean into the final view so that we could send it to the RateService
- Added the validation messages from /src/main/java/org/springframework/webflow/samples/shippingrate/domain/RateCriteriaValidator.java to the /src/main/webapp/WEB-INF/messages/messages_en.properties
- This is because RSF only allows internationalized messages
- We had to add one additional validation message because of an internal Spring exception message which RSF does not have access to
- 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/SWFShippingRate-SWFRSF-1.0/faces/
- Tested the various flows
Differences in app function#
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