Building Web Components Without a Component Framework Blog

Version 2



    What Is a Web Component?
    Two-Phase Request Processing
    The Lifecycle of a Standalone Component
    Single-State Component
    Multiple-State Component
    JSP Control
    JSP Controls Vs. Portlets
    JSP/Servlets Compatibility
    Creating JSP Components Using Struts
    Struts Dialogs
    DialogActionNavigation Rules
    Handling Error Messages
    Keeping State
    What About AJAX?
    Live Demos
    References and Source Code

    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?

    What Is a Web Component?

    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.

    Two-Phase Request Processing

    The central concept of the discussed approach is the splitting of one submit request into two phases: input and output. Thistwo-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:

    • The browser submits input data to a component.
    • The component processes the input, updates its state, and redirects the browser to the result location.
    • The browser loads a result page using another request.

    Because this pattern includes redirection, it can also be called"Redirect-After-Post"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, Ruby on Rails), or as an option (JSF).

    Two-phase request processing has three modes of operation:

    • With an initialization event, a component is prepared for use with new set of data.
    • In the input phase, a component accepts input data, updates the domain model, and generates error messages.
    • In the view rendering phase, a component generates a result page.

    The Lifecycle of a Standalone Component

    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 GETtype 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:

    • Using the component with a fresh set of data.
    • Navigating to component using a link.
    • Manually reloading a component using the browser's refresh button.
    • Automatically reloading a component as the second phase of request processing.

    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.

    Single-State Component

    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
    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 aGET 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.

    Multiple-State Component

    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
    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.

    JSP Control

    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
    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="/"/> </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. Master page with login control
    Figure 4. Parent page with login control; see the live demo

    Figure 4 shows a screenshot from a componentized JSP page with an embedded login dialog. (The page template is borrowed from theTemplatesBox 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.

    JSP Controls Vs. Portlets

    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, 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:

    • The browser makes an HTTP request to the portal.
    • The portal determines if the request contains an action targeted to any of the portlets associated with the portal page.
    • If there is an action targeted to a portlet, the portal requests that the portlet container invoke the portlet to process the action.
    • A portal invokes portlets, through the portlet container, to obtain content fragments that can be included in the resulting portal page.
    • The portal aggregates the output of the portlets in the portal page and sends the portal page back to the client.

    Now consider the similar lifecycle for a JSP control:

    • The browser submits input data directly to the JSP control.
    • JSP control processes input data, updates state, generates error messages if needed, and redirects to parent page.
    • The browser reloads the parent page, which pulls included controls using links; a link results in a GETrequest.
    • When controls receive a GET request, they render themselves, and their content fragments are included in the resulting page.
    • The JSP container aggregates the output of the controls in the parent page and sends the page back to the client.

    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=""><img src="contact.gif"></a> <% } else {%> <img src="blank.gif"> <% } %>

    JSP/Servlets Compatibility

    Using the <jsp:include> directive is a simple and efficient way to create JSP controls, but there is a catch. TheJava servlet specification, 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 ofWriter.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, 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, Resinworks 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 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 theTomcat/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

    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 theRequestDispatcher 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."

    Creating JSP Components Using Struts

    Now we can see how a JSP component can be created, using Struts. Why would one want to create components with Struts, 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 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 usingAction and ActionForm classes. AnAction class handles client requests, while anActionForm is populated with request data. To promoteActionForm from a simple transfer object to an interactive, stateful object, we need to set theActionForm scope to session instead ofrequest, and define behavioral methods in it. IfActionForm could process client events and store data between requests, it would look almost like a JSF UI component or aWebWorkaction.

    ActionForm cannot accept input events, butAction can. Therefore, a Struts web component can be built using a combination of these two classes.

    Figure 5. Struts component
    Figure 5. Struts component

    Figure 5 shows how the phases break down:

    • Input phase: 1) Populate the ActionFormwith 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.
    • Component reload: Redirect to the same action or to the parent page.
    • Render phase: 3) Forward to the JSP page, which reads data from the form bean.

    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. ButDispatchAction does not help to keep server state, store component properties, or handle error messages.

    Struts Dialogs

    This is where the Struts Dialogs library can be employed. (Note: The Struts Dialogs library is not affiliated with, or endorsed by, Struts or Apache.)

    The workhorse of the Struts Dialogs library is theDialogAction class. It extendsDispatchAction in several ways:

    • It works uniformly with pushbuttons and with image buttons.
    • It handles initialization events.
    • It buffers error messages.
    • It stores component state (in a corresponding action form).
    • It renders the view according to component state.

    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.

    DialogActionNavigation Rules

    When an HTML form is submitted, DialogActiondispatches 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 = "/" redirect = "true"/> <forward name = "DIALOG-VIEW" path = "/loginform.jsp"/> <forward name = "LOGIN-SUCCESS" path = "/" redirect = "true"/> </action>

    This is the same lifecycle for an embedded login control:

    <action path = "/login" <forward name = "DIALOG-RELOAD" path = "/" redirect = "true"/> <forward name = "DIALOG-VIEW" path = "/loginform-embedded.jsp"/> <forward name = "LOGIN-SUCCESS" path = "/" 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.

    Handling Error Messages

    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 inRequestProcessor, 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.

    Keeping State

    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 anActionForm. 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 companionActionForm 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 theICRUDForm interface. WizardAction, which builds web wizards, requires ActionForm to implement the IWizardManager interface.

    What About AJAX?

    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.

    Live Demos

    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.

    References and Source Code