|
Recipe 14.10. Integrating Struts and XSLTProblemYou want to use XSL transformations for HTML page generation instead of JSP pages in your Struts application. SolutionUse the STXX framework with Struts. DiscussionThe Struts for Transforming XML with XSL (STXX) framework was developed by Don Brown. (The STXX project site can be found at http://stxx.sourceforge.net.) STXX fits into Struts in a manner similar to Velocity. Instead of forwarding requests to JSP pages, your action forwards to a special URL that is processed by the StxxRequestProcessor. Based on request data, this custom request processor reads a configuration file to determine the corresponding XSLT stylesheet. The request processor then uses an XSLT transformation engine to transform the received XML data, using the XSLT stylesheet, into XHTML.
To get started, download STXX from the project web site (http://stxx.sourceforge.net). This recipe was built using the full download of STXX Version 1.3. Extract the download to your system. Copy the following JAR files to your application's WEB-INF/lib directory:
The configuration of STXX is specified in the stxx.properties file. For this recipe, you can use this file without modification. From the STXX directory, copy source/web/WEB-INF/classes/stxx.properties to your application's WEB-INF/classes directory. STXX uses the concept of pipelines to chain transformations together. The stxx-pipelines.xml controls how the pipelines work. For this recipe, copy the source/web/WEB-INF/stxx-pipelines.xml file to your application's /WEB-INF directory. As shown in Example 14-30, add an initialization parameter specifying the location of the stxx.properties file to the Struts ActionServlet declaration in your web.xml file. Example 14-30. Adding STXX to your application's web.xml<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> <display-name>Struts Cookbook - Chapter 14 : STXX</display-name> <!-- Standard Action Servlet Configuration (with debugging) --> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>stxxInit</param-name> <param-value>/stxx.properties</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <!-- Standard Action Servlet Mapping --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- The Usual Welcome File List --> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> You configure the integration between Struts and STXX in your Struts configuration file. Example 14-31 shows the struts-config.xml used for this sample application. Example 14-31. Integrating Struts and STXX in a Struts configuration file<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd"> <struts-config> <form-beans> <form-bean name="userForm" type="com.oroad.stxx.xform.JDOMForm"/> </form-beans> <global-exceptions> </global-exceptions> <global-forwards> </global-forwards> <action-mappings> <action path="/viewUserList" type="com.oreilly.strutsckbk.ch14.UserListAction"> <forward name="success" path="simple/viewUserList.dox"/> </action> <action path="/addUser" name="userForm" scope="request" forward="simple/addUser.dox" /> <action path="/saveUser" type="com.oreilly.strutsckbk.ch14.SaveUserAction" name="userForm" scope="request"> <forward name="success" path="/viewUserList.do"/> </action> </action-mappings> <message-resources parameter="ApplicationResources" factory="com.oroad.stxx.util.PropertyMessageResourcesFactory"/> <plug-in className="com.oreilly.strutsckbk.ch14.DataLoadPlugIn"/> <plug-in className="com.oroad.stxx.plugin.StxxPlugin" > <set-property property="pipeline-config" value="/WEB-INF/stxx-pipelines.xml" /> <set-property property="xmlform-models" value="/WEB-INF/xmlform-models.xml" /> <set-property property="xmlform-schema" value="" /> </plug-in> </struts-config> In a STXX application, your action form holds XML data. STXX provides two specialized classes that wrap XML data in an ActionForm. The contained XML can be represented as a traditional DOM object using the DOMForm or as a JDOM object using JDOMForm. JDOM provides a more natural API than DOM, so the JDOMForm was used in this recipe. <form-bean name="userForm" type="com.oroad.stxx.xform.JDOMForm"/> This form-bean element references an XML model that describes the XML structure of the form data. The XML models for your application are defined in the WEB-INF/xmlform-models.xml file. Example 14-32 shows the model, representing user information, used in this recipe. Example 14-32. XML models used by STXX<document> <model name="userForm"> <user> <name> <firstname /> <lastname /> </name> <email /> </user> </model> </document> A custom action in STXX performs the same functions as in a conventional Struts application: you retrieve data from the form, access the model, and forward to a destination. For STXX, the pattern can be specifically laid out as follows:
The actions defined in Example 14-31 represent a typical flow common to most web applications. The first action, /viewUserList, displays data on a page. The second action, /addUser, presents a form where a user can input data. The third action, /saveUser, saves the data in the model and forwards back to the first action. Here's the first action mapping: <action path="/viewUserList" type="com.oreilly.strutsckbk.ch14.UserListAction"> <forward name="success" path="simple/viewUserList.dox"/> </action> The UserListAction, shown in Example 14-33, retrieves data stored in application-scope and builds a JDOM document from it. The action saves the document in the request and forwards to "success." Example 14-33. Action that prepares an XML documentpackage com.oreilly.strutsckbk.ch14; import java.util.Iterator; import java.util.List; import com.oroad.stxx.action.Action; import javax.servlet.http.*; import org.jdom.*; import org.apache.struts.action.*; public class UserListAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { List usersList = (List) getServlet( ).getServletContext( ). getAttribute("users"); //create a new XML document for this Action with the root //element of "userList" Document document = new Document(new Element("userList")); //add some data to the XML document so that the Action //will produce XML in the form Element users = new Element("users"); for (Iterator k=usersList.iterator( ); k.hasNext( ); ) { Element user = new Element("user"); Element name = new Element("name"); User u = (User) k.next( ); name.addContent(new Element("firstname"). setText(u.getFirstName( ))); name.addContent(new Element("lastname"). setText(u.getLastName( ))); user.addContent(name); user.addContent(new Element("email"). setText(u.getEmail( ))); // add the user users.addContent(user); } // add to the root element and save the document document.getRootElement( ).addContent(users); saveDocument(request, document); return mapping.findForward("success"); } } The success forward specifies a path of simple/viewUserList.dox. The forward is processed by STXX and matched against patterns in the stxx-pipelines.xml file. In this example, the matching pattern defines a simple XML to XHTML transformation using an XSL stylesheet: <pipeline match="simple/*.dox"> <display-name>Simple XSLT</display-name> <description>Performs simple XSLT transformations</description> <transform type="html"> <param name="path" value="/xsl/{1}.xsl" /> <param name="render" value="server" /> </transform> </pipeline> The first param element specifies the context-relative path to the XSL stylesheet. The value contains a substitution value retrieved from the wildcard-matched path.
In this case, the transformation uses the xsl/viewUserList.xsl stylesheet shown in Example 14-34. Example 14-34. XSL stylesheet that renders the user list<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" /> <xsl:template match="/"> <xsl:apply-templates select="stxx/userList"/> </xsl:template> <xsl:template match="userList"> <html> <body> <table width="75%" border="1" align="center"> <tr> <td bgcolor="lightblue" align="center"> <a href="./xsl/viewUserList.xsl">View XSL</a> </td> <td bgcolor="lightblue" align="center"> <a href="./index.jsp"> <xsl:value-of select="/stxx/applicationResources/key[@name='link.index']"/> </a> </td> <td bgcolor="lightblue" align="center"> <a href="./addUser.do"> <xsl:value-of select="/stxx/applicationResources/key[@name='link.add.user']"/> </a> </td> </tr> <xsl:apply-templates select="users"/> </table> </body> </html> </xsl:template> <xsl:template match="users"> <tr bgcolor="lightgrey"> <td><b>First Name</b></td> <td><b>Last Name</b></td> <td><b>Email</b></td> </tr> <xsl:apply-templates select="user"/> </xsl:template> <xsl:template match="user"> <tr> <td><xsl:value-of select="./name/firstname"/></td> <td><xsl:value-of select="./name/lastname"/></td> <td><xsl:value-of select="./email"/></td> </tr> </xsl:template> </xsl:stylesheet> Figure 14-9 shows the page that gets displayed when you access the /viewUserList action. Figure 14-9. STXX-generated HTML pageClicking the "Add User" link sends a request to the /addUser action: <action path="/addUser" name="userForm" scope="request" forward="simple/addUser.dox" /> STXX processes this request through the xsl/addUser.xsl stylesheet shown in Example 14-35. Example 14-35. XSL stylesheet that generates an HTML form<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" /> <xsl:template match="/"> <html> <body> <form action="saveUser.do"> <table width="75%" border="1" align="center"> <tr bgcolor="lightgrey"> <td colspan="2"><b>Add User</b> <a href="./xsl/addUser.xsl"> (View XSL) </a> </td> </tr> <tr> <td><font color="red">*</font>First name:</td> <td> <input type="text" name="user/name/firstname" value="{stxx/form/userForm/user/ name/firstname}"/> </td> </tr> <tr> <td><font color="red">*</font>Last name:</td> <td> <input type="text" name="user/name/lastname" value="{stxx/form/userForm/user/ name/lastname}"/> </td> </tr> <tr> <td><font color="red">*</font>Email:</td> <td> <input type="text" name="user/email" value="{stxx/form/userForm/user/email}"/> </td> </tr> <tr align="center"> <td colspan="2"> <input type="submit" value="Submit"/> </td> </tr> </table> </form> </body> </html> </xsl:template> </xsl:stylesheet> Unlike a conventional Struts application, STXX doesn't use the Commons BeanUtils classes to populate the ActionForm. Instead, it treats the name attribute as an XPath expression into the XML form model specified for the action form. Figure 14-10 shows the rendered form for adding a user. Figure 14-10. STXX-generated HTML formThe /saveUser action receives and processes this form: <action path="/saveUser" type="com.oreilly.strutsckbk.ch14.SaveUserAction" name="userForm" scope="request"> <forward name="success" path="/viewUserList.do"/> </action> The SaveUserAction, shown in Example 14-36, extracts the data from the JDOM-backed form and updates the model stored in the servlet context. Example 14-36. Action that retrieves XML data from an action formpackage com.oreilly.strutsckbk.ch14; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import com.oroad.stxx.action.Action; import com.oroad.stxx.xform.JDOMForm; public class SaveUserAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { List usersList = (List) getServlet( ).getServletContext( ). getAttribute("users"); JDOMForm jdomForm = (JDOMForm) form; String firstName = jdomForm.getValue("/user/name/firstname"); String lastName = jdomForm.getValue("/user/name/lastname"); String email = jdomForm.getValue("/user/email"); usersList.add(new User(firstName, lastName, email)); return mapping.findForward("success"); } } The sample application shown in this recipe represents a fraction of the capabilities of STXX. STXX supports validation, XForms, SOAP, FOP, and Velocity just to name a few. If you have a site that relies heavily on XML-based data, and needs to render that data in a number of formats, STXX may be what you need. See AlsoThe STXX project web site (http://stxx.sourceforge.net) has complete details on the full functionality of STXX. The StrutsCX project (http://it.cappuccinonet.com/strutscx) is another popular Struts-XSL integration framework. |
|