Published on dev2dev (http://dev2dev.bea.com/)
http://dev2dev.bea.com/pub/a/2005/07/pageflows.html
See this if you're having trouble printing code examples
by Srinivas Jaini
07/18/2005
Struts is a popular framework used to build enterprise-level J2EE applications. With Struts, J2EE Web application development has become easier and more manageable. Beehive, an open-source project by the Apache Software Foundation, goes to great lengths to make Web application development even more straightforward by building a simple Page Flow model on top of Struts. Using the new JSR-175 and JSR-181 metadata annotation facilities, Beehive reduces the coding necessary for J2EE application development. This article introduces the Beehive Page Flow technology, and looks at how you can use it to increase the productivity and quality of Struts software. It also examines how you can migrate to include this technology in a vanilla Struts application. The article assumes you have some familiarity with Struts.
Struts, a part of the Jakarta project by the Apache Software Foundation, is an open-source framework for building Web applications based on the Model-View-Controller (MVC) design pattern. Struts uses action classes to build the controller component of a framework. A typical Struts application requires a number of action classes to handle several actions of a process flow and also an XML configuration file to declare forwards. Therefore, issues like data management and maintenance have become a major concern in existing Struts applications. Figure 1 shows a flow view of a sample application devised to make a comparative study between Struts and Page Flows.

Figure 1. Flow view of the LoginProcessFlow application
This application, when developed in Struts, typically will
require action classes for each of the actions begin,
signup, login, and logout.
Listing 1 shows a typical Action class, in
this case the begin
action depicted in the diagram.
Listing 1. BeginAction.java
package comparision.struts;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public final class BeginAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
return (mapping.findForward("success"));
}
}
Similar action classes for signup, login,
and logout are required to complete the
Struts application. The HTTP session is used to pass data across
actions, making it cumbersome to manage and maintain data. Struts
uses a configuration file, struts-config.xml,
to declare forwards to JSP pages. So a business rule change typically
requires changes to be made across action classes and the configuration
file. In particular, business logic changes require changes to be made
in action classes, and changes in flow require changes in the
configuration file (changes often require a combination of both).
Listing 2 shows a typical struts-config.xml
file for the LoginProcessFlow application.
Listing 2. struts-config.xml
<!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 Bean Definitions -->
<form-beans>
<form-bean name="UserForm" type="Samples.Struts.UserForm"/>
</form-beans>
<!-- Action Mapping Definitions -->
<action-mappings>
<action path="/Startup"
type="Samples.Struts.begin"
scope="request">
<forward name="login" path="/index.jsp"/>
</action>
<action path="/LoginForm" forward="/mypage.jsp"/>
<action path="/login"
type="Samples.Struts.Login"
name="LoginForm"
scope="request"
validate="false"
>
<forward
name="success"
path="/mypage.jsp"/>
</action>
</action-mappings>
<!-- message resources -->
<message-resources
parameter="ApplicationResources"
null="false" />
</struts-config>
|
Having looked at the complexity in managing a Struts application, let's take an inside look at Page Flows and what they offer. Beehive's NetUI Page Flows framework makes development and maintenance a lot easier and more manageable. Listing 3 is a Page Flow controller, often abbreviated to Java Page Flow (JPF), for the LoginProcessFlow application described earlier. The Page Flow framework replaces all the action classes and the configuration file with a single JPF file.
Listing 3. LoginPageFlowController.java
package comparison.pageflows;
import org.apache.beehive.netui.pageflow.PageFlowController;
import org.apache.beehive.netui.pageflow.Forward;
import org.apache.beehive.netui.pageflow.annotations.Jpf;
public class LoginPageFlowController extends PageFlowController
{
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/index.jsp")
}
)
protected Forward begin()
{
//initial processing
return new Forward("success");
}
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/mypage.jsp"),
@Jpf.Forward(name = "failure", path = "/index.jsp")
}
)
protected Forward login()
{
//authentication logic
if(user_authenticated)
return new Forward("success");
else
return new Forward("failure")
}
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/confirm.jsp")
}
)
protected Forward signup()
{
return new Forward("success");
}
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/begin.do")
}
)
protected Forward logOut()
{
return new Forward("success");
}
}
Listing 3 clearly shows the difference in the development effort and maintenance required between Struts and Page Flows.
As mentioned earlier, JPF is annotation driven and does not
require forwards to be declared in a configuration file. Page Flows
leverage the Beehive framework to auto-generate configuration files
based on the annotations, like @Jpf.Action,
declared in the JPF. Listing 3 shows how all the actions are handled in
a single JPF file along with the forwards declared easily in the form
of annotations. For example, the begin action,
on success, forwards to index.jsp. You can
clearly see how logic is handled by actions, and forwards are handled
by simple annotations declared above their place of use. Annotations
can be declared both at the action level and the Page Flow level. Of the
many annotations provided by Beehive, @Jpf.Forward,
@Jpf.Controller, and @Jpf.Action
are the most commonly used. Discussing all annotations is beyond the
scope of this article. For more information, visit the Page
Flow Annotations documentation.
While a Struts application requires action classes for each of the actions, a single JPF file will suffice to develop the application using Page Flows. Page Flows provide a simple, single-file programming model. Page Flows enable developers to become immediately productive in building sophisticated Web applications without the learning curve typically associated with Struts. This makes Page Flows more flexible when it comes to modifications driven by business rule changes.
|
The programming framework provided by Page Flows adds many enhancements, and you've just seen an example of the annotation-driven automatic generation and synchronization of XML configuration files. Let's now turn to another advantage: It is easier to manage and maintain data across actions using Page Flow-scoped variables.
In Struts, action classes get session objects and set attributes (or
put values) to be retrieved by other actions later. Listing 4 shows an
example of two Struts action classes, LoginAction
and LogOutAction, sharing a variable username
using the HTTP session.
Listing 4. LoginAction.java and LogOutAction.java
package comparision.struts;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public final class LoginAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
String username = form.getUserName();
// get this session
HttpSession session = request.getSession();
session.setAttribute("username", username);
return (mapping.findForward("login"));
}
}
package comparision.struts;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public final class LogOutAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
// get this session
HttpSession session = request.getSession();
String username = session.getAttribute("username");
//logic to log out user
return (mapping.findForward("logout"));
}
}
The above code clearly shows how the username variable must be explicitly shared by LoginAction and LogOutAction, in this case using the HTTP session. Managing such session variables across numerous action classes can get quite complex.
In contrast, Page Flows offer a simple variable declaration at
the JPF level that shares data across all Page Flow actions.
In the example in Listing 5, a String variable, username,
is declared at Page Flow level and is used in the login
and logout actions.
package comparision.pageflows;
import org.apache.beehive.netui.pageflow.PageFlowController;
import org.apache.beehive.netui.pageflow.Forward;
import org.apache.beehive.netui.pageflow.annotations.Jpf;
public class LoginPageFlowController extends PageFlowController
{
private String username;
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/index.jsp")
}
)
protected Forward begin()
{
//intial processing
return new Forward("success");
}
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/mypage.jsp"),
@Jpf.Forward(name = "failure", path = "/index.jsp")
}
)
protected Forward login(ProfileForm form)
{
username = form.getUsername();
//authentication logic
if(user_authenticated)
return new Forward("success");
else return new Forward("failure")
}
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/confirm.jsp")
}
)
protected Forward signup()
{
return new Forward("success");
}
@Jpf.Action(
forwards = {
@Jpf.Forward(name = "success", path = "/begin.do")
}
)
protected Forward logOut(ProfileForm form)
{
logout(username);
return new Forward("success");
}
}
As you can see, the handling of the shared variable is intuitive and straightforward.
|
Besides ease-of-use and other benefits that Page Flows offer, here are some important advantages of Page Flows:
Although this article is only an introduction to the basics, I hope you're getting a feel for how these features can contribute to creating large, manageable applications. The modularity and nesting features are particularly useful in composing reusable Web application components, while features such as session data management and rich data-binding tags contribute to ease of programming.
I hope the previous section has convinced you that Page Flows are a useful extension to Struts. Let's now look at ways in which you can add Page Flow functionality to existing Struts projects, or have Struts and Page Flow applications interoperate.
Page Flows are built on top of Apache Struts 1.1. Each Page Flow is compiled into a Struts module. As a result, Page Flow and Struts 1.1 applications can work closely together.
There are two approaches to using Struts components in Page Flow applications:
The Struts interoperability feature uses existing Struts-based components without modifying them. This allows existing Struts components to interact with Page Flow components and leverage existing JSP pages, action classes, forms beans, and flow configurations.
Struts and Page Flow applications can live and interact with each other inside a Web application. To forward from a Page Flow to a (pure) Struts module, simply reference the desired action within the Struts module. The same goes for the reverse direction: From a Struts module, simply configure an action to point to the desired method in the Page Flow.
A Page Flow can act as the front controller that processes the initial request and then passes the control over to actions within Struts that then do some processing and transfer control back to Page Flow. A similar pattern can be observed in nested Page Flows. Form beans can also be shared among Struts applications and Page Flows in these ways:
The files that comprise Struts modules and the Page Flows, if they are to interoperate, must exist in the same Web project.
Here are the requirements of Struts modules and Page Flows that will interoperate with each other in the same Web project:
<project-root>/strutsModule.
<form-bean name=...>
attribute must match exactly the value of the name attribute generated
in the Page Flow's jpf-struts-config-<pageflow-name>.xml
file. /WEB-INF/web.xml
file. This example shows how Page Flow actions can call Struts
actions and vice versa. The purpose of this example is simply to show interoperability,
and so the code is not complete. PageFlowActionA
passes control, along with session-scoped form bean, over to
StrutsActionA. StrutsActionA does some processing and forwards to
Page.jsp which has a Struts action,
StrutsActionB. StrutsActionB passes
control back to PageFlowActionB.
|
|
|
Struts merge converts simple Struts applications into Page Flow applications. This approach can be a fast way to move simple Struts applications to the Page Flow framework.
The Struts merge feature is different from the Struts interoperability feature. In the case of the Struts merge feature, a Struts XML configuration file can be specified at controller level as follows:
/**
* @jpf:controller struts-merge= "/WEB-INF/struts-config-merge-example.xml"
*/
public class ExampleStrutsMergeController extends PageFlowController
{
...
}
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 override Page Flow
defaults, or to specify settings for the Page Flow that are not
provided by Page Flow annotations or by their attributes. The Struts merge
files typically should be small and only modify the Page Flow and its
actions and form beans. Though action mappings can be added in the
Struts merge file, this practice is not recommended. 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.
You can also use the Struts merge feature to read configuration data from a pure Struts application into the Page Flow application's configuration files. Ordinarily, a Page Flow's configuration files are generated entirely from the application's Java source files (specifically from the metadata annotations that decorate the controller files). But, in cases of integration of a Struts module into an application, the configuration files can be generated from both the Java source files and the Struts module's configuration files. In that case, you have flexibility to change or add any tag in the generated configuration file. For example, an action form's default scoping can be overridden from request-scoping to session-scoping. You can do this simply by creating a Struts configuration file that overrides the appropriate parts of the Page Flow's configuration file, and then refer to this override file from within the Page Flow's Java source file, as indicated in the example above.
Both the Struts merge and Struts interoperability features can be used in a Page Flow for best results, taking into consideration parameters like complexity of existing Struts, time, and effort.
The Apache Beehive Project contains a lot more than Page Flows. It has three sub-projects:
Java Page Flows have full support for Java controls, a simple programming model abstraction for accessing enterprise resources and packaging business logic. Java controls give developers access to the powerful features of the J2EE platform (security, transactions, and asynchrony) in a simple, intuitive environment with methods, events, and properties, and enable developers to build Web applications conforming to best practices for service-oriented architectures (SOAs). For example, a Web service control can be declared in a Page Flow with a simple annotation as shown below:
@Control
public MyWebService myWebService;
The control will then be automatically injected in the Page Flow by the environment.
In combination with XMLBeans, Beehive provides infrastructure to seamlessly build and deploy enterprise SOAs. Beehive 1.0 is scheduled for release later this month, and plans for 2.0 are in progress. Beehive applications can be developed using IDEs like BEA WebLogic Workshop, or using Eclipse plug-ins being developed within the Pollinate project.
Page Flows come with a strong promise to make enterprise software development and maintenance easier and quicker. In this article, we examined some of the advantages of Page Flows over Struts, how Page Flows can enhance existing Struts applications, and several methodologies, including Struts interoperability and Struts merge, you can use to upgrade and seamlessly build enterprise-level SOA business applications.
Srinivas Jaini is a software architect with heavy experience in SOA and in building enterprise wide portals on J2EE and the WebLogic Platform for banking and other industries. He has a passion for technology and music.
Return to dev2dev.