http://dev2dev.bea.com/pub/a/2004/01/Dew.htmlWeb application development is hard. Or rather, Web application development used to be hard. Web application development used to be an activity that required developers to learn and use complex programming models. Web application development used to be an activity that required developers to manage the myriad details of configuring their Web application so that the various pieces of the application worked together. Web application development used to be an activity that was performed outside of the helpful environment of an IDE.
No longer.
The new
Java Page Flow feature set of WebLogic Workshop 8.1 beta makes Web application development easy. Java Page Flow provides a simplified, easy-to-understand Web application programming model. Java Page Flow automatically manages Web application configuration details. And Java Page Flow provides a set of tools that help developers to quickly and correctly build Web applications and to integrate those applications with business logic.
What is it?Java Page Flow is a feature set built upon a Struts-based Web application programming model. Java Page Flow leverages the power and extensibility of Struts while also eliminating the difficulties and challenges of building Struts-based applications. Java Page Flow features include runtime support for the Web application programming model and tools that enable developers to quickly and easily build applications based upon the model.
The central concept and construct of Java Page Flow is called
page flow. Basically, a page flow is a directory of Web app files that work together to implement a UI feature. For example, a page flow could implement a Web app’s user registration wizard feature. The files of such a page flow could be arranged in a
userRegistration
directory as follows:
The
userRegistration
directory contains several
*.jsp
files, a
*.jcs
file and a
*.jpf
file. The
*.jsp
files are standard Java Server Pages files that contain markup that describes the visual aspect of the user registration wizard. For example,
name.jsp
contains markup that describes a
First Name
and
Last Name
data entry form. The
*.jcs
file is a BEA innovation. It contains annotated Java code that implements logic used by the user registration wizard. For example, UserManager.jcs contains code that implements a
createUser( )
function. The *.jpf file is also a BEA innovation and is the main focus of this article. It contains annotated Java code that implements the navigation and state management logic of the user registration wizard and that makes calls to business logic. For example,
UserRegistrationController.jpf
contains code that decides that
name.jsp
should be presented before
address.jsp,
that gathers the
firstName
and
lastName
information from
name.jsp
before presenting
address.jsp
, and that calls the
createUser( )
function of
UserManager.jcs
.
For those who are familiar with Struts and the Model-View-Controller (MVC) way of programming, the files of the user registration wizard may be mapped to the 'M', the 'V' and the 'C' of MVC this way:
Model ( M ) |
UserManager.jcs |
View ( V ) |
name.jsp address.jsp confirmation.jsp
|
Controller ( C ) |
UserRegistrationController.jpf |
Page Flow ControllersPage flow controllers are the main focus of this article. So, let’s take a step-by-step look at the contents of the
UserRegistrationController.jpf
file and how the file performs its navigation, state management, and logic-calling functions.
First of all,
UserRegistrationController.jpf
contains a class definition whose skeleton looks like this:
package userRegistration;
import com.bea.wlw.netui.pageflow.PageFlowController;
public class UserRegistrationController
extends PageFlowController
{
...
}
|
UserRegistrationController
extends
PageFlowController
, which is a class that provides useful base class functionality and that derives indirectly from
org.apache.struts.action.Action
. The base class functionality of
PageFlowController
provides support for things such as login and logout. The derivation from
Action
is an important aspect of the Struts interoperability of
PageFlowController
. Many classes in Java Page Flow inherit from Struts classes and interoperate with the Struts plumbing that serves as the foundation for the Java Page Flow runtime.
Actions and NavigationBecause
UserRegistrationController
acts as the controller for the user registration wizard, it contains code that performs the page navigation decisions for the wizard. This code is in the form of action methods. Action methods are methods that are invoked in response to things like form submissions. For example,
UserRegistrationController
contains an action method named
name_next
:
package userRegistration;
import com.bea.wlw.netui.pageflow.Forward;
import com.bea.wlw.netui.pageflow.PageFlowController;
public class UserRegistrationController
extends PageFlowController
{
/**
* @jpf:action
* @jpf:forward name="getAddress"
* path="address.jsp"
*/
public Forward name_next( ... )
{
...
return new Forward("getAddress");
}
...
}
|
The
name_next
action method is invoked whenever a user submits the form of the
name.jsp
page:
<%@ taglib uri="netui-tags-html.tld" prefix="netui"%>
<html>
<head>
<title>Name Page</title>
</head>
<body>
<netui:form action="name_next" focus="">
<table>
<tr>
<th align="right" valign="top">First Name:</th>
<td align="left">
<netui:textBox dataSource="{actionForm.firstName}"
size="16"
maxlength="18"/>
</td>
</tr>
<tr>
<th align="right" valign="top">Last Name:</th>
<td align="left">
<netui:textBox dataSource="{actionForm.lastName}"
size="16"
maxlength="18"/>
</td>
</tr>
<tr>
<td align="right"></td>
<td align="left">
<netui:button value="name_next"/>
</td>
</tr>
</table>
</netui:form>
</body>
</html>
|
The
name_next
action method decides that the
address.jsp
page should be the next page presented to a user. Because the
name_next
action method is invoked whenever a user submits the form of the
name.jsp
page, the
name_next
method effectively causes navigation from the
name.jsp
page to the
address.jsp
page.
All action methods have similar signatures. For example, they all have
Forward
as their return type. All action methods are specially annotated with
@jpf:action
to indicate to the
*.jpf
compiler that they should be configured as action methods in the auto-generated Struts configuration files. Also, all action methods may be annotated with
@jpf:forward
to indicate to the *.jpf compiler and to the IDE tools the possible navigation decisions that the action methods may make such as deciding to forward to a page like
address.jsp.
All action methods are called by the page flow runtime and express their navigation decisions to the runtime by returning objects of type
Forward
to the runtime. The
Forward
objects encapsulate information described by the
@jpf:forward
annotations on the action methods.
The page flow runtime is responsible for selecting and calling action methods in controllers as part of the runtime’s request processing lifecycle. The page flow runtime is also responsible for executing the navigation decisions made by the action methods. In other words, if a user submits the form of
address.jsp.
, the page flow runtime performs a request processing lifecycle that includes these steps:
- Intercept the submission when it arrives at the application server
- Determine that the
name_next
action method should be called
- Call the
name_next
method
- Receive the
Forward("getAddress")
object from the name_next
method
- Forward to the
address.jsp.
page described by the Forward("getAddress")
object
Page flow controllers may contain any number of action methods. For example,
UserRegistrationController
contains five action methods in addition to the
name_next
action method:
package userRegistration;
import com.bea.wlw.netui.pageflow.Forward;
import com.bea.wlw.netui.pageflow.PageFlowController;
public class UserRegistrationController
extends PageFlowController
{
...
/**
* @jpf:action
* @jpf:forward name="getName"
* path="name.jsp"
*/
public Forward begin( ... )
{
...
return new Forward("getName");
}
/**
* @jpf:action
* @jpf:forward name="getAddress"
* path="address.jsp"
*/
public Forward name_next( ... )
{
...
return new Forward("getAddress");
}
**
* @jpf:action
* @jpf:forward name="getConfirmation"
* path="confirmation.jsp"
*/
public Forward address_next( ... )
{
...
return new Forward("getConfirmation");
}
/**
* @jpf:action
* @jpf:forward name="done"
* returnAction="userRegistrationDone"
*/
public Forward confirmation_next( ... )
{
...
return new Forward("done");
}
/**
* @jpf:action
* @jpf:forward name="getName"
* path="name.jsp"
*/
public Forward address_back( ... )
{
...
return new Forward("getName");
}
/**
* @jpf:action
* @jpf:forward name="getAddress"
* path="address.jsp"
*/
public Forward confirmation_back( ... )
{
...
return new Forward("getAddress");
}
}
|
FormsIn addition to selecting and calling action methods, the page flow runtime also passes to the action methods any form data that may have been submitted. For example, when a user submits the form of the
name.jsp
page, the page flow runtime captures the
firstName
and
lastName
data of the form in properties of a
NameForm
object, and passes the populated
NameForm
object to the
name_next
action method:
package userRegistration;
import com.bea.wlw.netui.pageflow.FormData;
import com.bea.wlw.netui.pageflow.Forward;
import com.bea.wlw.netui.pageflow.PageFlowController;
public class UserRegistrationController
extends PageFlowController
{
...
/**
* @jpf:action
* @jpf:forward name="getAddress"
* path="address.jsp"
*/
public Forward name_next( NameForm form )
{
...
return new Forward("getAddress");
}
public static class NameForm
extends FormData
{
private String firstName;
private String lastName;
public String getFirstName()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
}
...
}
|
State ManagementJava Page Flow provides a powerful yet easy-to-use foundation for state management that makes it simple for
UserRegistrationController
to perform its state management responsibilities. For example, to manage the form data that is received from pages such as
name.jsp
, code may be added to
UserRegistrationController
that captures the data in convenient instance members:
package userRegistration;
import com.bea.wlw.netui.pageflow.FormData;
import com.bea.wlw.netui.pageflow.Forward;
import com.bea.wlw.netui.pageflow.PageFlowController;
public class UserRegistrationController
extends PageFlowController
{
...
public String firstName;
public String lastName;
/**
* @jpf:action
* @jpf:forward name="getAddress"
* path="address.jsp"
*/
public Forward name_next( NameForm form )
{
firstName = form.getFirstName();
lastName = form.getLastName();
return new Forward("getAddress");
}
public static class NameForm
extends FormData
{
private String firstName;
private String lastName;
public String getFirstName()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
}
...
}
|
Because the page flow runtime makes it possible to cache state in controller instance members, it’s very easy to implement support for a back button in a wizard. For example, if the
confirmation.jsp
page contained a back button,
UserRegistrationController
could respond to a press of the back button by navigating backward to the
address.jsp
page with all previously-filled-out
address.jsp
form data still intact:
package userRegistration;
import com.bea.wlw.netui.pageflow.FormData;
import com.bea.wlw.netui.pageflow.Forward;
import com.bea.wlw.netui.pageflow.PageFlowController;
public class UserRegistrationController
extends PageFlowController
{
...
public String firstName;
public String lastName;
public String street;
public String city;
public String state;
public String zip;
/**
* @jpf:action
* @jpf:forward name="getConfirmation"
* path="confirmation.jsp"
*/
public Forward address_next( AddressForm form )
{
street = form.getStreet();
city = form.getCity();
state = form.getState();
zip = form.getZip();
return new Forward("getConfirmation");
}
/**
* @jpf:action
* @jpf:forward name="getAddress"
* path="address.jsp"
*/
public Forward confirmation_back( )
{
AddressForm addressForm = new AddressForm();
addressForm.setStreet(street);
addressForm.setCity(city);
addressForm.setState(state);
addressForm.setZip(zip);
return new Forward("getAddress", addressForm);
}
public static class AddressForm
extends FormData
{
private String street;
private String city;
private String state;
private String zip;
public String getStreet()
{
return street;
}
public void setStreet(String street)
{
this.street = street;
}
public String getCity()
{
return city;
}
public void setCity(String city)
{
this.city = city;
}
public String getState()
{
return state;
}
public void setState(String state)
{
this.state = state;
}
public String getZip()
{
return zip;
}
public void setZip(String zip)
{
this.zip = zip;
}
}
...
}
|
As shown, the
confirmation_back
action method forwards backwards to the
address.jsp
page. As part of the forward,
confirmation_back
passes to
address.jsp
the address data that had already been received from
address.jsp
as an argument to the
address_next
action method.
Calling Business LogicWebLogic Workshop makes it easy to expose business logic as controls, and the Java Page Flow feature makes it easy to call controls from action methods. Because the page flow runtime performs automatic instantiation and initialization of controls for all instance members that are annotated with @common:control, action methods in page flow controllers may call Workshop controls without having to include code that performs control instantiation and initialization. For example, the
confirmation_next
action method can simply and directly call the
createUser( )
method of
UserManager.jcs:
package userRegistration;
import com.bea.wlw.netui.pageflow.FormData;
import com.bea.wlw.netui.pageflow.Forward;
import com.bea.wlw.netui.pageflow.PageFlowController;
public class UserRegistrationController
extends PageFlowController
{
...
public String firstName;
public String lastName;
public String street;
public String city;
public String state;
public String zip;
/**
* @common:control
*/
public UserManager userManager;
/**
* @jpf:action
* @jpf:forward name="done"
* returnAction="userRegistrationDone"
*/
public Forward confirmation_next( ... )
{
userManager.createUser(firstName,
lastName,
street,
city,
state,
zip);
return new Forward("done");
}
...
}
|
WebLogic Workshop makes it easy to expose all kinds of business logic as controls, including business logic that is implemented as EJBs. In other words, WebLogic Workshop even makes it easy to build Web applications over EJBs.
Additional FeaturesJava Page Flow contains many features in addition to those described above. For example, you may have noticed that the @jpf:forward annotation for the
confirmation_next
action method was a bit different than all of the other @jpf:forward annotations:
package userRegistration;
import com.bea.wlw.netui.pageflow.Forward;
import com.bea.wlw.netui.pageflow.PageFlowController;
public class UserRegistrationController
extends PageFlowController
{
...
/**
* @jpf:action
* @jpf:forward name="done"
* returnAction="userRegistrationDone"
*/
public Forward confirmation_next( ... )
{
...
return new Forward("done");
}
}
|
The
returnAction="userRegistrationDone"
attribute enables
UserRegistrationController
to participate in a Java Page Flow feature that is called "nesting".
We’ll take a look at nesting as well as other Java Page Flow features such as data binding and custom tag support in a future dev2dev article.
ToolsAs mentioned, the Java Page Flow feature set includes some IDE tools that do much to help developers build Web applications. An example of such a tool is the new flow view that enables developers to architect entire Web applications using little more than simple drag-and-drop gestures:
Like nesting, data binding, and custom tags, we’ll take a look at the IDE tools in a future dev2dev article.
Take It for a Test SpinFor now, the best thing you can do is to download WebLogic Workshop 8.1 beta, experiment with the new Java Page Flow features, and see for yourself just how much easier Web application development can be.