Struts Support

1. Introduction

Struts is a popular Web Application framework, based on the MVC pattern. In recent years, Struts has quickly gained popularity due to its ease of use and conformance to the Model 2 pattern.

This chapter demonstrates how to use PresentationServer in conjunction with a Struts-based Web Application in a pattern called Model 2X. It assumes that the reader is familiar with Struts.

Please refer to this page for more information about the use of XSLT in place of JSP.

2. Control Flow

The following figure presents the flow of the request, from the client to the Struts servlet and PresentationServer pipeline:

  1. HTTP Request
  2. Controller Servlet creates the Form Bean
  3. Controller Servlet calls the Action Class
  4. The Action Class creates and fills in the Result Bean(s)
  5. The Request is forwarded to the PresentationServer Servlet
  6. The PresentationServer Servlet serializes the Result Bean in XML
  7. An PresentationServer pipeline is applied on the XML Bean
  8. HTML is sent back to the client

3. Implementation

A Struts-PresentationServer Web Application contains two servlets: the Struts controller server and the PresentationServer processor servlet. The former is the default servlet, serving all URI, while the latter is bound to xpl files and other resources served by PresentationServer (images, css, etc.). Here is a Web Application descriptor that instantiates both servlets:

  <web-app>
  <context-param>
  <param-name>oxf.resources.factory</param-name>
  <param-value>
org.orbeon.oxf.resources.PriorityResourceManagerFactory
  </param-value>
  </context-param>
  <context-param>
  <param-name>oxf.resources.webapp.rootdir</param-name>
  <param-value>/WEB-INF/resources</param-value>
  </context-param>
  <context-param>
  <param-name>oxf.resources.priority.1</param-name>
  <param-value>
org.orbeon.oxf.resources.WebAppResourceManagerFactory
  </param-value>
  </context-param>
  <context-param>
  <param-name>oxf.resources.priority.2</param-name>
  <param-value>
org.orbeon.oxf.resources.ClassLoaderResourceManagerFactory
  </param-value>
  </context-param>
  <context-param>
  <param-name>oxf.properties</param-name>
  <param-value>oxf:/config/properties.xml</param-value>
  </context-param>
  <servlet>
  <servlet-name>struts</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>
  <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet>
  <servlet-name>oxf</servlet-name>
  <servlet-class>org.orbeon.oxf.servlet.OXFServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
  <servlet-name>struts</servlet-name>
  <url-pattern>/</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
  <servlet-name>oxf</servlet-name>
  <url-pattern>*.xpl</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
  <servlet-name>oxf</servlet-name>
  <url-pattern>*.css</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
  <servlet-name>oxf</servlet-name>
  <url-pattern>*.js</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
  <servlet-name>oxf</servlet-name>
  <url-pattern>*.gif</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
  <servlet-name>oxf</servlet-name>
  <url-pattern>*.png</url-pattern>
  </servlet-mapping>
  </web-app>

Warning
The Struts servlet mapping parameter is set to:
org.apache.struts.action.RequestActionMapping
This instructs Struts to put the result bean in the request rather than the session.

The Struts controller servlet interprets the request and creates form beans containing the request parameters. It then instantiates and calls the appropriate action class. The result bean is then set in the request, and the request is forwarded to the PresentationServer processor servlet. Struts chooses the forwarding pages in the same way as the traditional JSP forward: through forward commands in the struts-config.xml file. For example, the following fragment shows a Struts action with one possible forward, the hello.xpl pipeline:

  <action path="/hello" type="org.orbeon.oxf.struts.examples.hello.HelloAction" name="hello">
  <forward name="success" path="/hello.xpl"/>
  </action>

PresentationServer utilizes a Bean generator to serialize beans from the request or session into XML. This process makes use of the Castor XML Marshaller.

The PresentationServer processor servlet is configured to use a simple pipeline that looks at the request's path and executes the corresponding pipeline, feeding it with the bean form generator. The pipeline's output is then processed through a second pipeline, the epilogue. This allows to apply common stylesheets ot all pages, such as style, internationalization, etc. The main pipeline is also responsible to serve static resources, such as images or css style sheets. Theses resources are served via the ResourceServer. You will find below the complete source of the Struts main pipeline. The same file, struts.xpl is also available with the struts examples bundled in PresentationServer distribution.

  <p:config xmlns:p="http://www.orbeon.com/oxf/pipeline">
  <p:param name="data" type="output"/>
  <p:processor name="oxf:request">
  <p:input name="config">
  <config>
  <include>/request/request-path</include>
  </config>
  </p:input>
  <p:output name="data" id="path"/>
  </p:processor>
  <p:processor name="oxf:xslt">
  <p:input name="config">
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
  <config>
oxf:<xsl:value-of select="substring-after(/request/request-path, '/WEB-INF')"/>
  </config>
  </xsl:template>
  </xsl:stylesheet>
  </p:input>
  <p:input name="data" href="#path"/>
  <p:output name="data" id="url"/>
  </p:processor>
  <p:processor name="oxf:url-generator">
  <p:input name="config" href="#url"/>
  <p:output name="data" id="user-pipeline"/>
  </p:processor>
  <p:processor name="oxf:xslt">
  <p:input name="data" href="aggregate('root', #user-pipeline, #url)"/>
  <p:input name="config">
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
  <p:config>
  <p:param name="data" type="output"/>
  <xsl:for-each select="/root/p:config/p:param[@type='input' and @name != 'errors']">
  <p:processor name="oxf:bean-generator">
  <p:input name="mapping">
  <mapping/>
  </p:input>
  <p:input name="config">
  <config>
  <attribute>
  <xsl:value-of select="@name"/>
  </attribute>
  <source>request</source>
  </config>
  </p:input>
  <p:output name="data" id="bean-{@name}"/>
  </p:processor>
  </xsl:for-each>
  <xsl:if test="/root/p:config/p:param[@type='input' and @name = 'errors']">
  <p:processor name="oxf:struts-errors-generator">
  <p:output name="data" id="errors"/>
  </p:processor>
  </xsl:if>
  <p:processor name="oxf:url-generator">
  <p:input name="config">
  <config>
  <xsl:value-of select="/root/config"/>
  </config>
  </p:input>
  <p:output name="data" id="user-pipeline"/>
  </p:processor>
  <p:processor name="oxf:pipeline">
  <p:input name="config" href="#user-pipeline"/>
  <xsl:for-each select="/root/p:config/p:param[@type='input' and @name != 'errors']">
  <p:input name="{@name}" href="#bean-{@name}"/>
  </xsl:for-each>
  <xsl:if test="/root/p:config/p:param[@type='input' and @name = 'errors']">
  <p:input name="errors" href="#errors"/>
  </xsl:if>
  <p:output name="data" ref="data"/>
  </p:processor>
  </p:config>
  </xsl:template>
  </xsl:stylesheet>
  </p:input>
  <p:output name="data" id="pipeline"/>
  </p:processor>
  <p:processor name="oxf:pipeline">
  <p:input name="config" href="#pipeline"/>
  <p:output name="data" ref="data"/>
  </p:processor>
  </p:config>

4. Writing a Struts View Pipeline

The struts.xpl pipeline presented in the previous section acts as a dispatcher. When the Struts servlet forwards the request to the PresentationServer servlet, this pipeline is executed. It loads, connects and executes the view pipeline for each page. The view pipeline must conform to the following contract:

  • It must have one data output
  • It can have any number of input. Each input is automatically connected to a Bean Generator, serializing a bean in the request. The name of the input determines the name of the request or session property where the bean is stored.
  • It can have an errors input. This input is connected to a Struts ActionError generator. If no errors are available, an empty errors element is generated. The error messages come from the Struts application resources and can be internationalized. Refer to the Struts ActionError documentation for more information.

5. Accessing the Struts Application Resources

PresentationServer provides a mechanism to access Struts resources from an XSLT stylesheet. You define a resource bundle in struts-config.xml, in the message-resource element. You can declare several resource bundles by specifying a key attribute.

  <message-resources parameter="org.orbeon.oxf.struts.examples.ApplicationResources" null="false" key="messages"/>

You access the resource bundle with the struts:message() function. This function takes 6 parameters, but only the first one is mandatory.

                    struts:message(key, bundle, arg0, arg1, arg2, arg3, arg4)
                
Parameter Description Default Value
key The message key of the requested message, which must have a corresponding value in the message resources Mandatory
bundle The name of the application scope bean under which the MessageResources object containing your messages is stored. Globals.MESSAGE_KEY
arg0 First parametric replacement value, if any. null
arg1 Second parametric replacement value, if any. null
arg2 Third parametric replacement value, if any. null
arg3 Fourth parametric replacement value, if any. null
arg4 Fifth parametric replacement value, if any. null

XSLT processors use a slightly different mechanism to access the Struts message library. PresentationServer provides wrappers for Xalan, Saxon 6 and 7. XSLTC calls the Java method directly. Declare the struts namespace and import the specified stylesheet to be able to call the struts:message() function. The table below shows the namespaces and stylesheets to import for the supported XSLT processors.

Namespace Stylesheet Function
Xalan http://www.orbeon.com/oxf/struts oxf:/oxf/struts/struts-support-xalan.xsl message()
Saxon 6 http://www.orbeon.com/oxf/struts oxf:/oxf/struts/struts-support-saxon.xsl message()
Saxon 7 and 8 http://www.orbeon.com/oxf/struts oxf:/oxf/struts/struts-support-saxon7.xsl message()
XSLTC http://xml.apache.org/xalan/java/org.orbeon.oxf.util.StrutsUtils N/A messageTag()

The following example calls the Struts support library for Xalan. It displays the content of the page.title resource key.

  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="oxf:/oxf/struts/struts-support-xalan.xsl"/>
  <xsl:template match="/root/beans/guess">
  <xhtml:html xmlns:xhtml="http://www.w3.org/1999/xhtml">
  <xhtml:head>
  <xhtml:title>
  <xsl:value-of select="struts:message('page.title')"/>
  </xhtml:title>
  </xhtml:head>
  </xhtml:html>
  </xsl:template>
  </xsl:stylesheet>

6. Using the Struts Validator

The Struts validator allows you to write Javascript code to validate user input on the browser. Struts uses the html:javascript tag to display the necessary Javascript code.

Similarly, PresentationServer exposes this functionality through an XSLT function. Only the first parameter is mandatory.

                    struts:javascript(formName, dynamicJavaScript, staticJavaScript, method, page)
                
Parameter Description Default Value
formName The key (form name) to retrieve a specific set of validation rules Mandatory
dynamicJavaScript Whether or not to render the dynamic JavaScript (boolean) true
staticJavaScript Whether or not to render the static JavaScript (boolean) true
method The alternate JavaScript method name to be used instead of the default one. The default is 'validate' concatenated in front of the key (form name) passed in (ex: validateRegistrationForm) null
page The current page of a set of validation rules if the page attribute for the field element in the xml file is in use. null

XSLT processors use a slightly different mechanism to access the Struts javascript library. PresentationServer provides wrappers for Xalan, Saxon 6 and 7. XSLTC calls the Java method directly. Declare the struts namespace and import the specified stylesheet to be able to call the struts:javascript() function. The table below shows the namespaces and stylesheets to import for the supported XSLT processors.

Namespace Stylesheet Function
Xalan http://www.orbeon.com/oxf/struts oxf:/oxf/struts/struts-support-xalan.xsl javascript()
Saxon 6 http://www.orbeon.com/oxf/struts oxf:/oxf/struts/struts-support-saxon.xsl javascript()
Saxon 7 and 8 http://www.orbeon.com/oxf/struts oxf:/oxf/struts/struts-support-saxon7.xsl javascript()
XSLTC http://xml.apache.org/xalan/java/org.orbeon.oxf.util.StrutsUtils N/A javaScriptTag()

The following example shows how to call the struts:javascript from a simple Xalan template.

  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="oxf:/oxf/struts/struts-support-xalan.xsl"/>
  <xsl:template match="/">
  <xhtml:html xmlns:xhtml="http://www.w3.org/1999/xhtml">
  <xhtml:head>
  <xhtml:script type="text/javascript">
  <xsl:value-of select="struts:javascript('jsTypeForm')"/>
  </xhtml:script>
  </xhtml:head>
  <xhtml:body>
...
  </xhtml:body>
  </xhtml:html>
  </xsl:template>
  </xsl:stylesheet>