Page flows in WebLogic Workshop 8.1 are based on Struts 1.1, which is a part of the Jakarta™ Project by the Apache Software Foundation™. The programming framework that is provided by WebLogic Workshop page flows adds many enhancements, including the automatic generation and synchronization of XML configuration files, plus a sophisticated graphical IDE to define, build, deploy, and maintain the web application.
WebLogic Workshop provides interchangeable support for Struts modules and page flow controller classes working together in the same web project. This feature, called "Struts Interop," extends the reach of both types of applications:
The files that comprise your Struts modules and the page flows, if they are to interoperate, must exist in the same web project. Unless there are conflicts with the names of page flow directories, you should be able to use your existing hierarchy of directories for the files that are used by the Struts module. However, the class or JAR files must reside in specific directories, as noted in Requirements of the Struts Modules and Page Flows.
The Struts Interop feature is different from the Struts Merge feature described in Merging Struts Artifacts Into Page Flows. In the case of the Struts Merge feature, you place a @jpf:controller struts-merge="..." annotation in your page flow JPF file. This step enables an existing, all-Struts XML configuration file to be merged (at project compilation time) with the page flow's generated jpf-struts-config-<pageflow>.xml file. The purpose of the Struts Merge feature is to enable you to override page flow defaults, or to specify settings for the page flow that are not provided by page flow annotations or their attributes. The Struts merge files should typically be small and only modify the page flow and its actions and form beans. While you could, for example, add action mappings in the Struts merge file, BEA does not recommend this practice. Struts action mappings should be declared in the page flow's .jpf file with @jpf annotations, and then (if necessary) modified with the Struts merge file.
In the case of the "Struts Interop" feature, the support allows your legacy Struts modules and your new page flows to exist in the same web project, and allows them to interact by using form beans.
You can, of course, use both the Struts Merge and Struts Interop features in a page flow. For detailed information, see A Struts Interop and Struts Merge Example in this topic.
Here are the requirements of Struts modules and page flows that will interoperate with each other in the same web project:
<init-param> <param-name>config/pageFlowOne</param-name> <param-value>/someDir/struts-config-pageFlowOne.xml</param-value> </init-param>
The prior example shows an incorrect setup that would result in a naming conflict. All your page flow actions will not be found.
import payroll.PayrollForm;
You can use page flow inner-class form beans from Struts modules. The <form-bean...> element would be defined as it is in the generated jpf-struts-config-<pageflow-name>.xml file. For example:
<form-bean type="foo.fooController$MyForm" name="myForm"/>
The following example combines Struts Merge and Struts Interop features to demonstrate the interoperability between page flows and Struts. The strutsInteropController page flow instantiates a form bean, JpfFormBean, then passes this form to a Struts module named strutsModule. The Struts module then alters the contents of the form and passes it back to the page flow. Because page flow forms derive from Struts forms, this sharing of the form bean is possible.
By default, page flows scope form bean instances to the request. But because in this example we want to use session-scoping to share the form bean between the page flow and the struts module, strutsModule, we will cause the page flow to operate on a session-scoped form bean. We do this by using the struts-merge attribute on the @jpf:controller annotation in the strutsInteropController.jpf source file. This annotation will cause the contents of the specified Struts XML file to be merged-in with the generated jpf-struts-config-strutsInterop.xml file.
The form bean we use in this example has a single field, "field1". At each step, the action (whether page flow or Struts) will update the value of "field1" so you can see that the same instance of the form is being passed around.
To demonstrate the "Struts Merge" and "Struts Interop" features in one example, we created the following entities. All these files are part of the sample application that is installed to the following location:
<WEBLOGIC_HOME>/samples/workshop/SamplesApp/WebApp/...
The file specifications shown in the following list are relative to the web project's root directory.
/strutsInterop/StrutsInteropController.jpf
When you examine this file's source, notice how we are importing the externally defined JpfFormBean form bean. Also notice how we used a class-level annotation, @jpf:controller struts-merge="merge-jpf-struts-config.xml", to merge in session-scoped form beans with the Struts configuration XML that will be generated for your page flow. As a result of the merger, JpfAction1, JpfAction2, and JpfAction3 are available for use in the page flow. Note that by default, page flow form bean instances are scoped for the request. Because our processing will include an entity beyond the page flow's boundary, the easiest way to share it will be to scope it to the session, as we move from (1) the page flow, (2) to the Struts module, and then (3) back to the page flow.
/strutsInterop/JpfFormBean.java
It will be instantiated by the page flow and passed to Struts module, strutsModule, then passed back to the page flow. At each step the value of Field1 will be changed to demonstrate that the same instance of the form is being passed between page flow and Struts.
/WEB-INF/src/strutsModule/Struts1.java
/WEB-INF/src/strutsModule/Struts2.java
When you examine the Struts1.java source, notice that we import the JpfFormBean that will be shared by the page flow and the Struts module. Struts1 is the first action in the Struts module and effectively causes the page flow runtime system to "bootstrap" or load the Struts module. Then the Struts1 action simply forwards to the Jsp2.jsp page, which then renders in the context of the Struts module, strutsModule.
In the source for Struts2.java, notice that we import the JpfFormBean that will be shared by the page flow and the Struts module. The Struts action method sets a new value for Field1, and then forwards back to the page flow.
/WEB-INF/struts-config-strutsModule.xml
In the strutsInterop page flow:
/strutsInterop/Jsp1.jsp
/strutsInterop/Jsp3.jsp
/strutsInterop/done.jsp
For the Struts module, we created a JSP page that is not part of the page flow, but interoperates with it:
/strutsModule/Jsp2.jsp
/strutsInterop/merge-jpf-struts-config.xml
The purpose of merging the Struts XML is to set the scope of the form beans to session, instead of the default request. It is worth repeating the following explanation. By default, page flows scope form bean instances to the request. Because in this example we want to share the form bean between the page flow and the Struts module strutsModule, we will scope the form bean to the session. We do this by using the struts-merge attribute on the @jpf:controller annotation in the strutsInteropController.jpf source file. This class-level annotation will cause the contents of the following XML file to be merged in with the generated jpf-struts-config-strutsInterop.xml file.
<init-param> <param-name>config/strutsModule</param-name> <param-value>/WEB-INF/struts-config-strutsModule.xml</param-value> </init-param>
Next we compiled the web project that contains these entities, by selecting the WebApp project name, executing a right-mouse click, and choosing Build Project. Then we started the server and, in a browser, accessed this URL:
http://localhost:7001/WebApp/strutsInterop/strutsInteropController.jpf
Now let's walk through the processing. It should be helpful to start with an accounting of the field1 values in the shared form. The following table shows the values, the order in which each value was set, which entity set each value, in which rendered JSP page the value is seen, and the page flow or Struts context.
Field1 String Value | Set By... | Rendered in... |
"Form bean Field1 default value set by the form bean itself." | The shared form bean, /strutsInterop/JpfFormBean.java | /strutsInterop/Jsp1.jsp (A page flow JSP) |
"Form bean Field1 value set by the page flow controller class." | The page flow controller class, /strutsInterop/strutsInteropController.jpf |
/strutsModule/Jsp2.jsp (A Struts JSP) |
"Form bean Field1 value set by the Struts2 class." | The Struts2 action class, /WEB-INF/src/strutsModule/Struts2.java | /strutsInterop/Jsp3.jsp (A page flow JSP) |
The following diagram illustrates the processing flow. In this diagram, the shaded boxes represent entities that are part of the page flow. The unshaded boxes represent Struts entities.
Here are the steps:
<action-mappings> <action validate="false" type="strutsInterop.strutsInteropController" name="jpfFormBean" path="/jpfAction1" scope="session"> <forward contextRelative="true" path="/strutsModule/strutsAction1.do" name="gotoStruts"/> </action> <action validate="false" type="strutsInterop.strutsInteropController" name="jpfFormBean" path="/jpfAction2" scope="session"> <forward path="/Jsp3.jsp" name="gotoPg3"/> </action> <action validate="false" type="strutsInterop.strutsInteropController" name="jpfFormBean" path="/jpfAction3" scope="session"> <forward contextRelative="true" path="/strutsInterop/done.jsp" name="gotoDone"/> </action>
These actions are now available for use in the page flow.
http://localhost:7001/WebApp/strutsInterop/strutsInteropController.jpf
<netui:form action="jpfAction1"> <netui:label value="{actionForm.field1}"/> </netui:form> <br/> <netui:anchor action="jpfAction1">Continue</netui:anchor>
In the page flow, jpfAction1 is defined as follows:
/** * @jpf:action * @jpf:forward name="gotoStruts" path="/strutsModule/strutsAction1.do" */ protected Forward jpfAction1(JpfFormBean inForm) { inForm.setField1(this.FORM_VALUE); return new Forward("gotoStruts"); }
In the form bean, the initial value for the actionForm context of field1 is set to a String, as follows:
public class JpfFormBean extends com.bea.wlw.netui.pageflow.FormData { public final static String FORM_VALUE = "Form bean Field1 default value set by the form bean itself."; private String field1 = this.FORM_VALUE;
Thus the initial form content on the rendered Jsp1.jsp (part of the page flow) displays the String. The following sample screens show only the top and bottom portions of the rendered Jsp1.jsp page:
...
Remember that Jsp1.jsp is managed by the page flow context. The first entity to set a value for field1 in the form was the form bean itself, as noted earlier. Again, the jpfAction1 method was defined as follows, and included the bolded line shown here:
/** * @jpf:action * @jpf:forward name="gotoStruts" path="/strutsModule/strutsAction1.do" */ protected Forward jpfAction1(JpfFormBean inForm) { inForm.setField1(this.FORM_VALUE); return new Forward("gotoStruts"); }
Thus as navigation control passes from /strutsInterop/Jsp1.jsp to /strutsModule/Jsp2.jsp, the value of field1 that was set in the page flow controller class is passed, too. This value was set in the page flow with the following statement:
public static final String FORM_VALUE = "Form bean Field1 value set by the page flow controller class.";That string is displayed on Jsp2.jsp when it is rendered by the server. The pre-rendered JSP includes the following:
<%@taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %> <%@ page import="org.apache.struts.action.ActionForm" %> <%@ page import="strutsInterop.JpfFormBean" %>
...
<% JpfFormBean tmpForm = (JpfFormBean) request.getSession().getAttribute("jpfFormBean"); out.write(tmpForm.getField1()); %>
...
<html:link action="strutsAction2">Continue</html:link>
Because the scope for the jpfAction1 form bean in the page flow was set to session, the scriplet you see in the Struts module's Jsp2.jsp can get the session context value for field1 from the shared form bean. That is why we see the following red text in the rendered Jsp2.jsp page:
But how did we go from Jsp1.jsp in the page flow, to Jsp2.jsp in the Struts module? Let's focus now on how that happened. Remember that the page flow jpfAction1 method was annotated with this @jpf:forward:
/** * @jpf:action * @jpf:forward name="gotoStruts" path="/strutsModule/strutsAction1.do" */ protected Forward jpfAction1(JpfFormBean inForm) { inForm.setField1(this.FORM_VALUE); return new Forward("gotoStruts"); }
The web project is running with registered servlets. One of the servlets that we registered in the /WEB-INF/web.xml is:
<!-- Declare struts module: strutsModule --> <init-param> <param-name>config/strutsModule</param-name> <param-value>/WEB-INF/struts-config-strutsModule.xml</param-value> </init-param>
In the Struts module, the strutsAction1 action is defined as follows:
<action path="/strutsAction1" name="jpfFormBean" scope="session" validate="false" type="strutsModule.Struts1" > <!-- The forward below forwards to a JSP that is part of the Struts module --> <forward name="gotoJsp2" contextRelative="true" path="/strutsModule/Jsp2.jsp" /> </action>
Notice how the Struts module's action mapping for /strutsAction1 contains the following line:
type="strutsModule.Struts1" >
That line identifies a Struts action class. In our example, the source is located in /WEB-INF/src/strutsModule/Struts1.java. When this action class runs, it returns a "gotoJsp2" forward name. Back in the Struts module, that forward name corresponds to:
<forward name="gotoJsp2" contextRelative="true" path="/strutsModule/Jsp2.jsp" />
So effectively, when users clicks the Continue link on the rendered Jsp1.jsp, they are forwarded to Jsp2.jsp.
Here is a summary about this important step. When the page flow jpfAction1 forwards to (path="/strutsModule/strutsAction1.do") the page flow runtime system gets this request and scans its list of Struts modules, looking for a Struts module called "strutsModule". As noted earlier, page flows are implemented as Struts modules. The page flow runtime finds the Struts module "strutsModule" because we declared it in the project's /WEB-INF/web.xml file. The page flow then bootstraps, or loads, the Struts module by processing the "struts-config-strutsModule.xml" file, which was also specified in the web.xml file. The runtime now has information about all the action mappings, forms, and so on, for this Struts module. It has the Struts module context. Next, the page flow runtime instantiates the Java class specified by the (type="strutsModule.Struts1") attribute in the /WEB-INF/struts-config-strutsModule.xml (the context) file and calls its "execute" method, passing in the form bean instance that was located in the session.
<action path="/strutsAction2" name="jpfFormBean" scope="session" validate="false" type="strutsModule.Struts2" > <!-- The forward below returns to the *PAGE FLOW* --> <forward name="gotoJpf3" contextRelative="true" path="/strutsInterop/jpfAction2.do" /> </action>
We are operating in the Struts module context. In /WEB-INF/src/strutsModule/Struts2.java, a new value for FORM_VALUE (field1) is set:
public final static String FORM_VALUE = "Form bean Field1 value set by the Struts2 class.";
With that new value set and the user's click of the Continue link, the strutsAction2 action forwards to the jpfAction2.do action that is defined in the page flow. When we run that action in the page flow, it results in a forward to the page flow's Jsp3.jsp, as shown in this extract from the page flow controller class:
/** * This action method was raised by an action in the struts module: strutsModule. * * @jpf:action * @jpf:forward name="gotoPg3" path="Jsp3.jsp" */ protected Forward jpfAction2(JpfFormBean inForm) { return new Forward("gotoPg3"); }
Note: Struts2 forwards to (path="/strutsInterop/jpfAction2.do"). The runtime looks for a Struts module "strutsInterop" and finds the page flow "strutsInterop". Again, page flows are implemented as Struts modules. However, page flows that you create do not have to be declared in the project's /WEB-INF/web.xml. The runtime automatically registers Struts modules that are generated from page flows.
Notice how the rendered Jsp3.jsp, which is part of the page flow, displays the field1 form value that was set by the Struts2 action class:
The remaining processing is simply the JpfAction3 action that is raised by the Continue link on Jsp3.jsp. It forwards to the page flow's done.jsp page.
For information about merging the configuration XML, see:
Merging Struts Artifacts Into Page Flows