- You want page flows and Struts modules to exist in same Web project.
- You want a page flow and a preexisting Struts module to work together.
- You want to use Struts features that are not directly supported by page flows.
This article assumes that you are familiar with both Struts and page flows. In it, we will cover the coexistence and interaction between page flows and Struts modules, and the merging of Struts declarations into page flows.
Overview: Page Flows on top of Struts
Want to learn more about page flows and Struts? Download the author's sample application, which accompanies this article. Every page flow (.jpf) file turns into a Struts module at runtime. During compilation of a page flow, a Struts module configuration XML file is generated and written to /WEB-INF/jpf-struts-config-<directory>.xml. This module is automatically registered with Struts when the first user request for the page flow is made (the module does not need to be registered explicitly in web.xml, as long as the Web project uses the Page Flow
DynamicSubappActionServlet as the action servlet). When actions are raised, the PageFlowRequestProcessor locates the right page flow (Action) instance to which to delegate for action handling.Struts and Page Flows, Side-by-side
Struts 1.1 modules can be used alongside page flows in a Web project. If a Struts module requires that a different action servlet be used, then the Page Flow
DynamicSubappActionServlet can be safely replaced, with the caveat that page flow modules will need to be registered in web.xml according to standard Struts practice.Interaction Between Page Flows and Struts Modules
Forwarding between a page flow and a Struts module is simple. From a page flow, you declare a forward to an action in a Struts module:
/**
* @jpf:action
* @jpf:forward name="strutsBegin" path="/strutsModule/begin.do"
*/
public Forward goStruts()
{
return new Forward( "strutsBegin" );
}
|
Similarly, in the Struts module you forward to an action in the page flow (or to the page flow itself):
<action path="/runJpf"
type="org.apache.struts.actions.ForwardAction"
parameter="/pageflow/pageflowController.jpf"/>
<action path="/jpfBegin"
type="org.apache.struts.actions.ForwardAction"
parameter="/pageflow/begin.do"/>
|
(Of course, you could also write an Action class that returns an ActionForward pointing to the page flow.) Additionally, you can forward directly to a page flow JSP:
<action path="/showJpfPage"
type="org.apache.struts.actions.ForwardAction"
parameter="/pageflow/index.jsp"/>
|
In general, you should not forward directly to a Struts JSP from a page flow, because Struts does not support direct viewing of JSPs by default. A Servlet filter (
PageFlowJspFilter) is used to initialize the correct module when viewing a JSP that is associated with a page flow.Things get more interesting when you want a page flow and a Struts module to share form bean data with one another. Firstly, page flows accept
ActionForm-derived objects everywhere that FormData-derived objects are accepted. Form beans do not need to be defined as inner classes on the pageflow, and they do not have to be derived from FormData, which is a Page Flow extension of the Struts ActionForm. The following form bean type could be accepted by a page flow action method:
package someExternalPackage;
public class MyForm extends org.apache.struts.action.ActionForm
{
…
}
|
The following action method is then legal in a page flow:
/**
* @jpf:action
* @jpf:forward …
*/
public Forward useStrutsForm( someExternalPackage.MyForm form )
{
…
}
|
The easiest way to share form bean data between a page flow and a Struts module is to use session-scoped form beans. Page flow form beans are request-scoped by default, though, and we will have to change this in order to share data using session-scoped form beans. This leads us to the Struts-merge feature.
Merging Struts Declarations into Page Flows
Although a page flow's Struts module configuration file (/WEB-INF/jpf-struts-config-<directory>.xml) is regenerated each time the page flow is compiled, you can use the Struts-merge feature to add tags and change any tag in the generated file. Normally, the page flow module configuration file is generated wholly from the page flow source file:
You can, however, have input into this process by creating an override file that gets merged with the generated output:
To do this, you simply create another Struts configuration file that overrides pieces of the page flow's configuration file, and then refer to this override file from within the page flow in a special annotation. For example, if you want to change the form bean scoping for a page flow action, you would create the following XML file:
<struts-config>
<form-beans>
</form-beans>
<global-exceptions/>
<global-forwards/>
<action-mappings>
<action path="/sendToStruts" scope="session"/>
</action-mappings>
</struts-config>
|
This file contains just enough information to override the appropriate pieces of the page flow's generated configuration file - in this case, it overrides the
scope attribute on the action tag for the "/sendToStruts" action. Here is the page flow and its generated configuration file, without the Struts-merge enabled:
/**
* @jpf:controller
*/
public class pageflowController extends PageFlowController
{
/**
* @jpf:action
* @jpf:forward name="success" path="index.jsp"
*/
protected Forward begin()
{
return new Forward( "success" );
}
/**
* @jpf:action
* @jpf:forward name="send" path="/strutsModule/strutsShowForm.do"
*/
protected Forward sendToStruts( MyStrutsForm form )
{
return new Forward( "send" );
}
…
}
<struts-config>
<form-beans>
<form-bean
type="strutsModule.MyStrutsForm"
name="myStrutsForm"/>
</form-beans>
<global-exceptions/>
<global-forwards/>
<action-mappings>
<action
validate="false"
scope="request"
type="pageflow.pageflowController"
path="/begin">
<forward path="/index.jsp" name="success"/>
</action>
<action
validate="false"
scope=";request"
type="pageflow.pageflowController"
name="myStrutsForm"
path="/sendToStruts">
<forward
contextRelative="true"
path="/strutsModule/strutsShowForm.do"
name="success"/>
</action>
</action-mappings>
…
|
Now, to merge our override file, we simply add the
struts-merge attribute to @jpf:controller:/** * @jpf:controller struts-merge="/WEB-INF/merge-example.xml" */ |
Voila! The generated /WEB-INF/jpf-struts-config-pageflow.xml will now contain an updated configuration for the "/sendToStruts" action:
<action
validate="false"
scope="session"
type="pageflow.pageflowController"
name="myStrutsForm"
path="/sendToStruts">
<forward
contextRelative="true"
path="/strutsModule/strutsShowForm.do"
name="success"/>
</action>
|
Back to the data-sharing example. Now that the page flow "sendToStruts" action uses session-scoping for its form bean, it will share the same form bean instance with any Struts action that uses a form of the same name and type, like this one:
<action
path="/sendToJpf"
type="org.apache.struts.actions.ForwardAction"
parameter="/pageflow/showForm.do"
name="myStrutsForm"/>
|
Notes on Merging
- You can add any valid Struts tag, e.g., a
<plug-in>tag. - You can override any tag that is generated from the page flow. The
actiontag is merged using thepathattribute as a key,form-beanusingname,forwardusingname, andexceptionusingtype.
Example: Validation
The Struts ValidatorPlugIn allows you to declare form bean validation rules in an XML file. While page flows do not support the ValidatorPlugIn by default, you can use the Struts-merge feature to enable it. First, here is the page flow:
/**
* @jpf:controller
* struts-merge="/WEB-INF/strutsValidator-merge-config.xml"
* @jpf:message-resources resources="validation.validator.Messages"
*/
public class strutsValidatorController extends PageFlowController
{
/**
* @jpf:action
* @jpf:forward name="formPage" path="formPage.jsp"
*/
public Forward begin()
{
return new Forward( "formPage" );
}
/**
* @jpf:action validation-error-page="formPage.jsp"
* @jpf:forward name="success" path="success.jsp"
*/
public Forward submitForm( MyForm form )
{
return new Forward( "success" );
}
/**
* This form bean uses the ValidatorPlugIn (rules are in
* /WEB-INF/strutsValidator-validation.xml).
*/
public static class MyForm extends ValidatorForm
{
private String _email;
private String _age;
public String getEmail()
{
return _email;
}
public void setEmail( String email )
{
_email = email;
}
public String getAge()
{
return _age;
}
public void setAge( String age )
{
_age = age;
}
}
}
|
If form validation for the
submitForm action fails, formPage.jsp will be redisplayed; however, we have to use Struts-merge to enable the ValidatorPlugIn. Here is /WEB-INF/strutsValidator-merge-config.xml, which is referenced in the page flow's struts-merge attribute:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD
Struts Configuration 1.1//EN" "
http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<form-beans/>
<global-exceptions/>
<global-forwards/>
<action-mappings>
</action-mappings>
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathnames"
value="/WEB-INF/validator-rules.xml,/WEB-INF/strutsValidator
-validation.xml"/>
</plug-in>
</struts-config>
|
This file simply adds a
plug-in tag, which is merged in with the page flow's module configuration. The resulting generated /WEB-INF/jpf-struts-config-strutsValidator.xml follows:
<struts-config>
<form-beans>
<form-bean
type="validation.strutsValidator.strutsValidatorController$MyForm"
name="myForm"/>
</form-beans>
<global-exceptions/>
<global-forwards/>
<action-mappings>
<action
validate="false"
scope="request"
type="validation.strutsValidator.strutsValidatorController"
path="/begin">
<forward path="/formPage.jsp" name="formPage"/>
</action>
<action
validate="false"
scope="request"
type="validation.strutsValidator.strutsValidatorController"
path="/exit">
<forward
className="com.bea.wlw.netui.pageflow.config.PageFlowActionForward"
path="begin"
name="previousPageFlowBegin">
<set-property value="true" property="nestedReturn"/>
</forward>
</action>
<action
validate="true"
scope="request"
input="/formPage.jsp"
type="validation.strutsValidator.strutsValidatorController"
name="myForm"
path="/submitForm">
<forward path="/success.jsp" name="success"/>
</action>
</action-mappings>
<controller processorClass="com.bea.wlw.netui.pageflow.
PageFlowRequestProcessor"/>
<message-resources null="false"
parameter="validation.validator.Messages"/>
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathnames" value="/WEB-INF/validator-rules.xml,
/WEB-INF/strutsValidator-validation.xml"/>
</plug-in>
</struts-config>
|
Now, the Struts ValidatorPlugIn is enabled for this page flow.
Conclusion
Page flows not only live side-by-side with Struts modules, but they can share data with Struts modules and even integrate Struts features that are not supported directly by the page flow framework. Whether you're making page flows and Struts modules interact, or enabling Struts features such as the ValidatorPlugIn, or simply integrating page flows and Struts modules within the same project, you should find what you need. Happy merging!



