| ||||||||||||||||||
JSF is touted to be the ultimate component framework for Java web application programming. Tapestry claims to be based on the idea of component development. And across enemy lines, ASP.NET generated a whole new market for web components. What are web components and can they be developed with something more traditional like JSP and Struts?
A web component can be defined as a stateful server-side object that handles client events and input data, renders itself according to its state, and has a certain lifecycle. A web component can be embedded into another component, can contain other components, or can be standalone. Embedded components are often called controls, especially by the ASP.NET crowd.
Depending on the particular component architecture, the parent/child association can relate either to server objects or to visual representation. For example, JSF differentiates between a server-side component and a corresponding page widget, while Tapestry combines both component template and a Java class in one definition.
This article treats a web component in the old-fashioned way, as a resource that is identified with a unique location, accepts user input from request parameters, and is able to render itself.
What is new in that, you may wonder? Nothing but good old HTTP with a twist. If you use JSP as the presentation layer for your web applications, this article may open some new possibilities.
The central concept of the discussed approach is the splitting
of one submit request into two phases: input and output. This
two-phase request processing solves several issues related
to the submission of HTML forms, like implicit double submits, annoying
POSTDATA messages, and unfriendly user experience for reload, back,
and forward buttons. The two-phase request/response sequence looks
like this:
Because this pattern includes redirection, it can also be called "Redirect-After-Post" [19] or "Post/Redirect/Get." It must be understood that redirection is an inherent part of this pattern. Redirection allows splitting one solid "submit input data, respond with result page" process into two phases, which are completely independent from the server standpoint. This technique is implemented in several frameworks either as the main vehicle for component lifecycle handling (Wicket [20], Ruby on Rails [21]), or as an option (JSF [22]).
Two-phase request processing has three modes of operation:
A standalone component has a whole page for itself, so the address of the web page is the same as the location of a component. When a HTML form is submitted, the component handles the event, validates input data, and updates its state.
If there's an error, the component saves error data in the
session and redirects to itself. This is called component
reloading. Redirection to the same location does not cause an
infinite loop, because the new request is of the GET type and is "clean;"
that is, it does not contain query parameters. When the component
receives a "clean" GET request, it renders itself.
If no errors were detected, the component can either relinquish control to another component, or can reload itself and render a page corresponding to its new state. Redirection is a preferred way of passing control to prevent implicit resubmits.
Stateful web components must be explicitly initialized using a special request parameter. Otherwise, it would be impossible to distinguish between the following situations:
The initialization event offers a chance to reset and clean the
component, and to fill it with new data. Three other events--linking, manual refreshing, and automatic reloading--are treated in
the uniform fashion: the component receives a "clean" GET request
and renders a view corresponding to current state. This means that
navigating to the standalone component via a link displays the
current state of the component, but does not initialize it.
Figure 1 shows the a simplest case of a standalone component, which has only one state and one view. Such a component can be called a dialog. This particular dialog is used for user login. It collects the user name and password and validates them. If the login information is valid, it redirects to the user's home page. If invalid, the dialog generates an error message, temporarily stores it in the session, and reloads itself. After reloading, the dialog redisplays the invalid user name and password along with an error message. The cycle repeats until a user enters valid login information.

Figure 1. Standalone login dialog
The login dialog is a stateful object; it saves the user name and password between login attempts. On the other hand, the login dialog does not define an explicit state name, because there are no other states in which the login dialog can exist.
The login dialog is represented on a web page as an HTML form. When the form is rendered, its input fields are populated with dialog properties. When the form is submitted, the request sends form data directly to the login dialog.
It may appear that the dialog does not have to be initialized
explicitly: each time it is navigated to, it can be recreated. This
is not true. A dialog handles a request in two phases, so it is
impossible to distinguish the reload phase from navigating to it
using a web link. In both cases, the dialog is accessed with a GET
request, so there should be something different between the
requests that sets apart initialization and reload operations.
Therefore, all two-phase components, even simple ones, must be explicitly initialized. If the login page is the first page of a website, it will be initialized at creation time. Otherwise, a link to the login dialog should contain an initialization parameter.
Figure 2 shows a version of the standalone login component, which not only allows the user to log in, but also tracks the user's login status. This component has two different states: "not logged in" and "logged in," and two corresponding views.

Figure 2. Standalone multi-state login component
Whenever a logged-in user navigates to the login component location, the component displays user information and allows the user to log out.
Figure 3 shows a login component that is redesigned as part of a larger parent page. Unlike standalone components that can use any presentation technology, this child control is based on the JSP specification.

Figure 3. Embedded login control
The JSP control is embedded into the parent page using the
<jsp:include> tag:
<%@ page contentType="text/html;charset=UTF-8"
language="java" %>
...
<TABLE>
...
<TR>
<TD>
<jsp:include page="/login.do"/>
</TD>
</TR>
...
</TABLE>
As you can see, the control is dynamically included in the parent page; there are no additional parameters and no special API. The control renders itself when the parent page is being rendered.
As in previous examples, user input is submitted directly to the component instead of being channeled through a central page controller. But you may be asking: how can the embedded control redraw itself after its state changes? How can it force other components to redraw?
This can be facilitated with one small change in the request/response cycle: instead of reloading itself after processing user input, the JSP control reloads the parent page, which in turn pulls up all embedded components. Figure 4 shows an example of this approach.

Figure 4. Parent page with login control; see the live
demo [23]
Figure 4 shows a screenshot from a componentized JSP page with an embedded login dialog. (The page template is borrowed from the TemplatesBox [24] site.) This shows how a web page can be built from independent components without using complex component frameworks or special protocols like the portlet API. Good old HTTP provides enough to ensure proper component update and rendering, while the JSP specification allows including dynamic resources and sharing data between the components.
Most web frameworks define navigation rules in an external configuration file, so the child component may not even know that it was made part of a larger page.
It would be a stretch to compare an API consisting of one
<jsp:include> tag with the feature-rich Portlet
API. Nevertheless, there are similarities.
A portlet, as defined in the portlet specification [25], is a "Java technology based web component, managed by a portlet container, that processes requests and generates dynamic content." Similarly, a JSP control is a web component, managed by a JSP container, that processes requests and generates dynamic content.
The portlet specification states that the "content of a portlet is normally aggregated with the content of other portlets to form the portal page." The same can be said about the JSP control.
Users interact with content produced by portlets "by following links or submitting forms, resulting in portlet actions being received by the portal, which are forwarded by it to the portlets targeted by the user's interactions." This unlike the JSP control, which instead receives form submissions directly; a parent page may not even know that a request was dispatched to a control.
The portlet specification shows the following example of portlet lifecycle, initiated when users access the portal page:
Now consider the similar lifecycle for a JSP control:
GET request.GET request, they render themselves, and
their content fragments are included in the resulting page.Of course, besides visually embedding a component in a web page, the Portlet API provides many other features, like portlet preferences, user information, caching, security, access to persistent storage, packaging, etc. But sometimes visual aggregation of a component is enough. Returning to the login control, probably the only information that it needs to share with other components is that a user is logged in, and the name of the user.
This information exchange can be facilitated with a single
session attribute, like AUTH, having the user's name
as its value. When this attribute is set, it reports both the fact
that a user is logged in, and his or her name. This can be used,
for example, to show contact information links for registered users
only:
<% if (session.getAttribute("AUTH") != null) { %>
<a href="contact.do"><img src="contact.gif"></a>
<% } else {%>
<img src="blank.gif">
<% } %>
Using the <jsp:include> directive is a simple and
efficient way to create JSP controls, but there is a catch. The
Java servlet
specification [23], version 2.4, section 8.4, reads: "Before the
forward method of the RequestDispatcher interface returns, the
response content must be sent and committed, and closed by the
servlet container." This means that it is not legal to use
forwarding within an included resource. If the container catches us
using server-side forwarding, it has to close the response. As a
result, the portion of the parent page located below the included
component will not be rendered.
The current specification requires us to process input, update
the model, and render output, all at the same place. Basically,
this leaves a developer with two choices: either stick
data-processing code into the included JSP page, or add a bunch of
Writer.println statements into the included servlet.
Neither approach is pretty; no wonder
<jsp:include> is used mostly for simple headers
and footers.
Nevertheless, you can work around this issue. First of all, try your container. Tomcat [26], being a reference JSP implementation, closes responses exactly according to JSP spec, which does not work well for components that render output by forwarding to a JSP page. A patch, which keeps the response opened, is available, but it has not been tested for production environments.
On the other hand, Resin [27] works perfectly well. It does not close the response as the spec mandates, but instead keeps it open until the parent page is rendered completely. Jetty [28] is somewhere in between; it can be fooled by using a "proxy" JSP page.
Another option at first seems like a hack, but is quite reasonable for a stable codebase: using Java classes, automatically generated by the container from JSP pages. If you use Tomcat, then all you need is to copy the Java files generated by Jasper from the Tomcat/work directory into your project directory, and to compile them together with other project files. Instead of forwarding to a JSP page, you call the Java code. The code snippet below uses the class embedded_0002dchild_0002dlogin_jsp.java.
public ActionForward getView(...) {
HttpSession session = request.getSession();
HttpJspBase page =
new embedded_0002dchild_0002dlogin_jsp();
page.init(this.getServlet().getServletConfig());
page._jspService(request, response);
page.destroy();
return null;
}
Of course, hacking container code is not the best choice, so I hope that the JSP spec leads will recognize the need to update the discrepancies between the servlet and JSP specifications, and will change the paragraph quoted above to something like this:
"Before the forward method of the RequestDispatcher
interface returns, the response content must be sent and committed,
and closed by the servlet container, if the request was not included
in another request."
Now we can see how a JSP component can be created, using Struts. Why would one want to create components with Struts [29], when there are component-oriented frameworks like JSF?
The first reason is to get better control of the request/response cycle. Struts is a fairly thin wrapper over Java servlets, and allows you to do quite a lot with the HTTP protocol. A browser does not care about server technology, but it can use a good HTTP response.
The second reason is legacy. Why switch to another framework, if you can reuse the one that has served you well so far? Componentized Struts allows to use traditional JSP instead of learning another markup language.
The third reason is pure sport. The JSF FAQ [30] says: "Struts has no notion of server-side components, which also means that it has no event model for responding to component events and no facility for saving and restoring component state." This is true, but can be easily fixed.
Struts is a controller framework and handles requests using
Action and ActionForm classes. An
Action class handles client requests, while an
ActionForm is populated with request data. To promote
ActionForm from a simple transfer object to an
interactive, stateful object, we need to set the
ActionForm scope to session instead of request,
and define behavioral methods in it. If ActionForm
could process client events and store data between requests, it
would look almost like a JSF UI component or a WebWork [31] action.
ActionForm cannot accept input events, but
Action can. Therefore, a Struts web component can be
built using a combination of these two classes.

Figure 5. Struts component
Figure 5 shows how the phases break down:
ActionForm with request
values; 2) dispatch the input event to the handler method in
Action; process the input, update the component state,
and generate error messages if needed.The only class from the core Struts library that comes close to
component development is DispatchAction. It allows
the processing of submit events using handler methods. But
DispatchAction does not help to keep server state,
store component properties, or handle error messages.
This is where the Struts Dialogs [32]
library can be employed.
The workhorse of the Struts Dialogs library is the
DialogAction class. It extends
DispatchAction in several ways:
DialogAction defines several default keys, used for
event dispatching. Among them is a default event prefix,
DIALOG-EVENT; a mapping name for component reload,
DIALOG-RELOAD; and a mapping name for default view,
DIALOG-VIEW. The last is used by single-page dialogs.
DialogAction Navigation RulesWhen an HTML form is submitted, DialogAction
dispatches an event to a corresponding handler method. Automatic
validation must be turned off in a corresponding form bean. The
handler validates input data explicitly, and performs a model
update if needed. In case of an error, the handler saves errors in
the session and redirects to the same action, effectively reloading
the action. When DialogAction receives a "clean" GET
request, it renders a view.
This is how a lifecycle is defined for a standalone login dialog in struts-config.xml:
<action path = "/login"
...
<forward name = "DIALOG-RELOAD"
path = "/login.do" redirect = "true"/>
<forward name = "DIALOG-VIEW"
path = "/loginform.jsp"/>
<forward name = "LOGIN-SUCCESS"
path = "/userhome.do" redirect = "true"/>
</action>
This is the same lifecycle for an embedded login control:
<action path = "/login"
<forward name = "DIALOG-RELOAD"
path = "/parentpage.do" redirect = "true"/>
<forward name = "DIALOG-VIEW"
path = "/loginform-embedded.jsp"/>
<forward name = "LOGIN-SUCCESS"
path = "/parentpage.do" redirect = "true"/>
</action>
Instead of reloading itself, an embedded control reloads the parent page, which automatically reloads all controls. The same Java code is used for both standalone and embedded components.
Error messages are generated by a method that handles input events. Messages must be saved between requests, so the handler method stores messages in the session object. After the component reloads, the JSP page reads messages from the session.
DialogAction does not explicitly remove messages
from the session after the page has been rendered. Instead,
messages are cleared each time a new input event is processed.
Struts 1.2.6 introduced a new feature in
RequestProcessor, which removes messages from the
session after they have been accessed. In this case, messages will not be
redisplayed when the page is reloaded manually.
For simple dialogs, there is no need to define explicit state.
All that is needed is to store input/output data between requests,
which is facilitated by using session scope for an
ActionForm. For multi-state components, state either
has to be defined explicitly, or has to be calculated based on some
criteria.
DialogAction does not define a companion
ActionForm or specific component states, but some of its
subclasses do. For example, CRUDAction (which
implements create, edit, view, and delete operations for a business
object) requires ActionForm to implement the
ICRUDForm interface. WizardAction, which
builds web wizards, requires ActionForm to implement
the IWizardManager interface.
Two-phase request processing appears to be less important with the increasing acceptance of AJAX. But the idea of rendering a component whenever it is reloaded is still important, because some AJAX components do not expect that a parent page can be refreshed by a user.
Also, building a component for both AJAX and non-AJAX applications is easier when the non-AJAX application uses two-phase request processing. Therefore, two-phase request processing seems to be a valuable technology for web applications.
This article explained how to create simple stateful components and embedded page controls, using existing JSP technology. Components can be created fully in JSP, or with aid of your favorite web framework. I used Struts because I know it well, but you may try the one of your choice. Even if a framework has a different component architecture, like Wicket, it is usually possible to include a JSP fragment in a resulting page. This opens exciting possibilities of seamless integration of JSP components with existing and new web frameworks.
The following demos are built with the Struts framework and the Struts Dialogs library:
The samples are run on a development server, which can go offline from time to time.
Links:
[1] http://www.java.net/author/michael-jouravlev
[2] http://www.java.net/article/2005/08/02/building-web-components-without-component-framework#what_is_web_component
[3] http://www.java.net/article/2005/08/02/building-web-components-without-component-framework#two-phase_request
[4] http://www.java.net/article/2005/08/02/building-web-components-without-component-framework#lifecycle
[5] http://www.java.net/article/2005/08/02/building-web-components-without-component-framework#single-state_component
[6] http://www.java.net/article/2005/08/02/building-web-components-without-component-framework#multiple-state_component
[7] http://www.java.net/pub/a/today/2005/08/04/jspcomponents.html?page=2#jsp_control
[8] http://www.java.net/pub/a/today/2005/08/04/jspcomponents.html?page=2#jsp_controls_vs_portlets
[9] http://www.java.net/pub/a/today/2005/08/04/jspcomponents.html?page=3#jsp_servlets_compatibility
[10] http://www.java.net/pub/a/today/2005/08/04/jspcomponents.html?page=3#creating_jsp_components
[11] http://www.java.net/pub/a/today/2005/08/04/jspcomponents.html?page=3#struts_dialogs
[12] http://www.java.net/pub/a/today/2005/08/04/jspcomponents.html?page=4#dialogaction_navigation_rules
[13] http://www.java.net/pub/a/today/2005/08/04/jspcomponents.html?page=4#handling_error_messages
[14] http://www.java.net/pub/a/today/2005/08/04/jspcomponents.html?page=4#keeping_state
[15] http://www.java.net/pub/a/today/2005/08/04/jspcomponents.html?page=4#what_about_ajax
[16] http://www.java.net/pub/a/today/2005/08/04/jspcomponents.html?page=4#conclusion
[17] http://www.java.net/pub/a/today/2005/08/04/jspcomponents.html?page=4#live_demos
[18] http://www.java.net/pub/a/today/2005/08/04/jspcomponents.html?page=4#references_source_code
[19] http://www.theserverside.com/articles/article.tss?l=RedirectAfterPost
[20] http://wicket.sourceforge.net
[21] http://www.rubyonrails.com
[22] http://java.sun.com/j2ee/javaserverfaces
[23] http://www.java.net/%3Ccs_comment
[24] http://www.templatesbox.com/
[25] http://developers.sun.com/prodtech/portalserver/reference/techart/jsr168
[26] http://jakarta.apache.org/tomcat
[27] http://www.caucho.com
[28] http://jetty.mortbay.org/jetty
[29] http://struts.apache.org
[30] http://java.sun.com/j2ee/javaserverfaces/reference/faqs/#differences
[31] http://www.opensymphony.com/webwork
[32] http://struts.sourceforge.net/strutsdialogs
[33] http://www.superinterface.com/strutsdialog/logincomponent.html
[34] http://www.superinterface.com/strutsdialog/logincontrol.html
[35] http://struts.apache.org/
[36] http://wicket.sourceforge.net/
[37] http://api.rubyonrails.com/classes/ActionController/Base.html