JavaServer Pages is a hot technology right now, as all Java developers are aware. In its simplest explanation, JSP provides the ability to combine Java code with HTML content to achieve dynamic content output from a single source file. Behind the scenes the JSP is compiled into a Java servlet that can be run in any compliant Java servlet engine/container. In essence, a JSP is a way to dynamically create a servlet with a large amount of HTML output and some Java code/logic. So instead of putting a large number of out.println("<HTML CODE HERE>"); statements in a servlet with very little logic, a developer can simply create a JSP page containing standard HTML and a little logic. The JSP compiler will compile the page into a servlet, handling all the ugliness of those out.println() statements.
Given the nature of Web-based applications, this technology is obviously useful, but it comes with some potential costs. Embedding Java code within HTML can lead to maintenance nightmares, with huge files that require both an HTML "Web smith" and a Java developer to maintain them. A JSP framework that could provide a separation of the rather static content of the HTML code from the dynamic content typically provided by the Java code would be a boon.
Another problem with JSP for Web-based front ends and especially for application service providers such as CascadeWorks is the challenge of using a technology like JSP to create a client front end that emulates a lot of the functionality of a standard client application's GUI interface through the Web browser. In addition to needing a separation of HTML content from Java code, it would be nice to emulate the rather event-driven mechanisms and visual components of a GUI framework, such as the one provided by the Swing framework of the Java Foundation Classes Library (JFC), to try to achieve similar functionality for a Web-based interface. However, one of the problems we faced at CascadeWorks is that JSPs are essentially a "page-driven" technology. That created a few problems for us (which we'll address) and led to an interesting solution in creating a JSP framework that would allow pages to be constructed quickly through a template-like process using sets of standard components and events.
Building a Model-View-Controller Framework for JSPs
Most GUI frameworks use a Model-View-Controller (MVC) design pattern. This means that the data (Model) is separated from the visual presentation (View) of that data, and the two are tied together, typically, by a controller of some kind, which can be another class, an underlying set of events, both, and so on. In creating an MVC for a Web-based application using JSP, we must look at what's needed. We investigated many different attempts at such a framework, including the Struts project, which is part of the overall Jakarta/Tomcat open source project. Before we discuss our solution, though, a short digression on Struts is called for.
Struts is a Tomcat subproject whose goal is to "...provide an open source framework useful in building web applications with Java Servlet and JavaServer Pages (JSP) technology. Struts encourages application architectures based on the Model-View-Controller (MVC) design paradigm."
Struts provides a model for seemingly every possible HTML element as a JSP Taglib, which allows them to easily access and be accessed by the server code. Struts then ties every request to an action object through an ActionMapping. So when a request comes in to the server the perform() method on a specific action instance will be called. Struts also provides JSP Taglibs that perform logic that might be needed by the display, such as iterators and comparators. These Taglibs and the supporting framework solve the problem of having logic and therefore JSP scriptlets in the HTML page.
We faced another problem at Cascadeworks, though, that created a need beyond what a page-centric component model like this would support. We wanted to do cleanup for an included page after it was rendered but before the next included page was rendered. For this we needed a framework that would render an included page, then call an event for that page. This meant we needed to build up more full-fledged application functionality in a JSP framework, and for that we really need a full-fledged event model that breaks the page-centric model of JSP, something that's currently lacking in Struts.
Our JSP Framework Wish List
When looking at an MVC design for our JSP pages, there are a few things we'd like to have:
Looking at the first of these wish-list items we see that the JSP specification allows for including one JSP page within another. Let's look at the two ways it allows us to do this.
- The ability to support a container model so that one JSP page can be included within another with no modifications of either being necessary. This would help us create templates for including pages within pages, so that a page could contain headers, footers, menu bar columns, and so on, yet let us easily include or swap out individual pages in a "main" display area dynamically.
- An event model, in case we need to do any calculations before or after a page is displayed. In other words, we'd like to be able to fire an event just before a JSP page is displayed and just after. We'd also like to fire events on all the pages that make up one display (our frame in the source code) before we begin displaying the first page. So along with a preJspDisplay event per page, we'd also like to have a preWindowDisplay event, which is invoked for each frame and wraps a set of pages.
- The ability to support a component model so that commonly used HTML data input elements can be reused easily.
One way is to use a JSP directive, <%@ include file=... %>. When the JSP compiler comes across this directive it adds the interpreted code of the included file directly in the service() method of the servlet the compiler is creating.
In Tomcat's example for <%@include ...%>, the compiler generates the following code to insert foo.jsp inside include.jsp (removing the generated comments):
out.write("<!--\r\n Copyright (c) 1999
The Apache Software Foundation. All rights \r\n reserved.
\r\n-->\r\n\r\n<body bgcolor=\"white\">\r\n<font color=\"red\">\r\n\r\n");
out.print( System.currentTimeMillis() );
The other way is to use a JSP action, <jsp:include page=... />. When the JSP compiler comes across this directive it adds a pageContext.include() to the servlet the compiler is creating.
From the same example the following code is generated for the <jsp:include .../>:
String _jspx_qStr = "";
pageContext.include("foo.jsp" + _jspx_qStr);
The problem with both of these solutions is that neither allows for item number 2 in our wish list. We'd like to have our framework support an event model. In our case this means that before and after a page is displayed (written out to the output stream), we'd like to have the framework fire an event. Neither the include directive nor the include action allows us to support the kinds of events we want and have included pages. So to support a container model as well as an event model (of the type specified), we can't use the provided JSP include functionality.
We'll return to a specification for the event mechanism in more detail later. Now let's look at the third item on our wish list - the ability to have components. This can be relatively straightforward if we approach components the same way the Struts project did. Every common HTML element or sets of elements or even logic can be created as Taglibs with a reasonably simple interface into the server code. But what we'd really like is to have our framework fire events on the components as well, initializing them with data when necessary and storing out any of the data they've acquired when necessary (this would involve tying a binding mechanism to back-end storage).
Unfortunately, we were unable to solve this problem without, as was done at CascadeWorks, preregistering all the components and precompiling all the JSP pages. This is a separate and involved discussion and beyond the scope of this article. So we'll simply use the Struts' conception of components - it's a Taglib that's a display-oriented item with its own well-defined set of events that are specified in the JSP 1.1 spec and leave it at that.
Our MVC and EVENT Framework
We're going to put together a framework that uses our own mechanism for including pages and supports a full event mechanism. Components are more of an adjunct to the framework than an integral part of it, so we won't discuss them here beyond repeating that the Struts model is a good one.
Our framework starts by providing support for our container concept and event model to the JSP pages we'll be using. This support comes in the form of "backing" document objects. Every JSP page will have a corresponding document object associated with it and vice versa. The collection of JSP pages (remember, we're going to be including JSP pages within JSP pages) that make up the client's view will be modeled by a frame object, which is simply a container for the JSP page's backing document objects. See Listing 1 for an example of a JSP page in our model. (Code listings can be found on the JDJ Web site.)
This acts as the outer page for every single one of our displayed pages. It will always be displayed. In its turn it includes a JSP page that acts as a header and one that acts as the main page. Each of these documents may include other documents. Its backing document class is in Listing 2.
This class defines itself and the page it references, allowing the controller to access the backed JSP document through this class. It also extends the abstract class document, which defines all the events that may be called on the document (see Listing 3).
The controller will fire each of these events on every document with a JSP page to be displayed. Every backing document class also contains a ModelContext object, which allows it to access the ServletContext, HttpRequest, and HttpResponse objects of the current request.
The controller is the linchpin of this setup, driving our event model. It's the single access point of our system, loading every page, and works in conjunction with the frame to display the pages requested (see Figure 1).
The controller is represented by a single controller servlet that performs the following actions:
Note that in number 6 we support a special type of event - a user-defined type. For example, a user can press a button on a page, and the controller will call the defined event for that method on the specified document. Figure 2 shows the sequence of events within the framework.
- Creates a frame object that represents the HTML page to be displayed based on the incoming URL.
- Loads up the frame to be displayed.
- Initiates the framePreProcess() events on the document objects that make up the frame to be displayed.
- Initiates the display of the frame to be displayed.
a. The frame object that represents the frame to be displayed fires the pagePreProcess() event on each document object about to be displayed.
b. It gets the document's associated JSP file and displays it.
c. It fires the pagePostProcess() event on the just displayed Document object.
- Initiates the validate() events on the document objects that make up the previous displayed frame.
- Fires any user-initiated events on the document objects that make up the previous displayed frame.
- Initiates the framePostProcess() events on the document objects that make up the frame that was just displayed.
Two related problems remain unresolved. How does our framework allow pages to be used as a template (i.e., to have other JSP pages included within them), and how do we represent such a page in a URL so that our event mechanism actually works? Any page in our framework that wishes to include another page within it must use a special Taglib, include_document.
The include_document Taglib takes one attribute - location - which is simply a placeholder ID. Whichever JSP page is associated with that placeholder ID is included by a call to RequestDispatcher.include(). The association between the location attribute ID and the JSP page to be included is made for each frame to be displayed in an XML document. For example:
<?xml version="1.0" encoding="ISO-8859-1"?>
<doc location="MAIN" class="com.jdjarticle.jsp.document.MainDocument">
<doc location="HEADER" class="com.jdjarticle.jsp.document.HeadDocument"/>
<doc location="SUB_MAIN" class="com.jdjarticle.jsp.document.SubMainDocument">
<doc location="PAGE_01" class="com.jdjarticle.jsp.document.FirstDocument"/>
This XML document describes the documents each document will contain using the hierarchical nature of XML, and defines the placeholders each document will fill in their respective parent document/JSP pages.
URLs are then mapped to these XML documents in another XML document, called alias.xml.
This means that the URL controller/main will be mapped to the main.xml, which in turn allows the controller to create a frame object to display. Similarly, controller/second will be mapped to second.xml.
JavaServer Pages is a powerful technology that can provide many benefits to companies requiring dynamic Web front ends with maximum server-side flexibility. It's our hope that this article demonstrates ways in which the flexibility and features of JSP can be leveraged to provide more complex features, such as containers, document templates, and controller-based events, found in a typical GUI-style MVC design.
Scott Grant is chief architect and lead developer for CascadeWorks, Inc., and a Sun Certified Java developer. He has 15 years of diversified engineering experience..
[email protected] or
Joseph Campolongo, a senior developer with CascadeWorks, Inc., is a Sun Certified Java developer. [email protected].
Download Assoicated Source Files (Zip format - 282 KB)