Create black-box, reusable UI components from JSP pages and Java servlets Modularization and composition are perhaps the two most important features we have come to rely on in every modern programming environment. Server-side Java Web development technologies offer several means to decompose an application into reusable units. But encapsulating and reusing frontend logic remains one of the most challenging aspects of Web application design. Custom tags constitute one way of encapsulating presentation logic and reusing it within JavaServer Pages (JSP) pages. However, they have several notable disadvantages such as requiring a fair amount of Java programming expertise as well as recompilation and redeployment upon change. Usage of the standard server-side include mechanisms is more common. Includes are more accessible to Web authors and often appear as the more practical solution even to experienced Java developers. Still, the reuse usually achieved through includes rarely goes beyond the simple, macro-like textual level.In this article, I present a simple technique that enables treating JSP pages and servlets as self-contained, reusable presentation modules. While it opens new and interesting possibilities for decoupling large Web applications, it does not appear to be widely adopted. Based on the dynamic include mechanism of the Java Servlet and JSP specifications, the technique can be viewed as a pattern in the context of Web programming or as an implementation of the general calling frame pattern found in nearly all modern programming languages. I introduce a small API and tag library you can use in your own Web applications. For simplicity, I will use the general term dynamic resource to refer to both JSP pages and servlets.Note: You can download the source code that accompanies this article from Resources. Background and problemRecall that there are two basic means of including one JSP page from another: the <@include file="..."> directive and the <jsp:include page="..."> standard action. The include directive performs a translation time include of the specified file’s textual content. It can be considered simply as a type of macro facility. The include action, on the other hand, invokes the specified server-side resource at runtime. From a programming perspective, it is a form of a procedure call. For more on the differences between static and dynamic includes, please see Resources.Yet, to truly treat a JSP page or servlet as a standalone piece of logic, we would like to view it as a user interface (UI) component that can be configured and invoked to render itself. The invocation part is already handled by the dynamic include mechanism. Configuration is the trickier part. Because the dynamic include is implemented as a separate server-side request, you can always append a query string to the Uniform Resource Identifier (URI) and pass request parameters as you would do in a normal URL link. The standard include dispatching mechanism scopes the request parameters; the original request parameters are available with include-specific ones taking precedence in the case of a name conflict. However, there are a few limitations:No servlet API allows you to programmatically manage the parameters passed to the include, besides appending a query string to the URI pathYou can’t hide the original request parametersRequest parameter types can only be regular stringsThe first problem is admittedly a minor point in cases where the presentation is exclusively generated through JSP pages. The second becomes an issue in larger applications where resources are included from different contexts and their behavior depends on the presence (or absence) of a very specific set of request parameters. The last problem is the most problematic. It imposes a serious restriction on the kind of information one can pass to the included resource. Because of the third limitation in the above list, information between the various dynamic resources composing a request’s final output is usually passed around in the form of request-scoped attributes. It is then accessed within a JSP page through standard JSP actions or JSTL (JSP Standard Tag Library) EL (expression language) expressions. (Please see Resources if you are unfamiliar with the JSTL and its expression language.) But storing request attributes pollutes the global request namespace and frequently leads to subtle bugs. It imposes a heavy burden on both an included resource author and an including servlet author, as they both must be aware of all possible contexts in which the inclusion is performed and carefully manage the naming of attributes. For instance, if the included resource sets some request attributes, perhaps to include in turn another resource, how can you be sure it is not overriding a global attribute? Or, when it checks for presence of an attribute to decide on some action, how can you be sure that the attribute in question is present, but for entirely different purposes and/or with a completely different type? The list of potential problems goes on. In brief, it is very difficult to work with the dynamic resource as a black-box independent of the current global runtime context.Solution: Add calling frames to dynamic includesTo solve this problem, I apply an old trick and create a calling frame for the data passed to an invoked process and destroy it once that process completes execution. Given the standard runtime semantics of dynamic includes, which stipulate that the inclusion be performed in the same thread and with its own request instance, this approach is only natural. When performing an include of some JSP page or servlet, you pass to it your own request object with its own attributes map. The latter acts as a local calling frame similar to a stack frame in a conventional procedure call. After the inclusion is done, your custom request object can be released together with its set of local attributes.With this setup, the request instance of a dynamic resource has its own attribute namespace, and it is completely safe for authors to assume that upon return from the inclusion, the request object’s original state will remain untouched. Thus, a dynamic resource becomes a well-encapsulated functional unit that can be safely reused from various places as long as the caller complies with its “interface.” One does not have to manage a global namespace for the whole request processing. You can implement this strategy by writing a simple request replacement class and a few supporting custom JSP tags to use instead of the standard jsp:include action.ImplementationThe basic Servlet API for performing dynamic includes is the RequestDispatcher class. An instance can be obtained through a call to the current request instance’s getRequestDispatcher() method: RequestDispatcher dispatcher = request.getDispatcher(resource_uri); where resource_uri is the relative URI path to the desired resource. The actual include is done by calling: dispatcher.include(request, response); Notice that a request object is passed to the include() method. This is the request instance available to the included resource; it is also your opportunity to plug in a custom request.Your strategy is to implement your own version of the standard ServletRequest interface that wraps the current request object and maintains its own set of attributes. The wrapper class will delegate all method calls, except the ones dealing with setting and getting attributes, to the current request instance. A standard convenience implementation called class javax.servlet.ServletRequestWrapper, which does most of the work for you, already exists. You just extend it and override the methods of interest, namely the attributes map manipulation methods. Listing 1 shows this code. Listing 1public class IncludeServletRequestWrapper extends ServletRequestWrapper { private boolean hide_scope = true; private HashMap attributes = new HashMap(); public IncludeServletRequestWrapper(ServletRequest request) { super(request); } public IncludeServletRequestWrapper(ServletRequest request, boolean hideScope) { super(request); this.hide_scope = hideScope; } /** * Specify whether the enclosed request scope should be available. In other * words, specify whether attributes of the wrapped request should * be searched. * * @param hideScope <code>true</code> if attributes of the * wrapped request should remain hidden and <code>false</code> otherwise. */ public void setHideScope(boolean hideScope) {this.hide_scope = hideScope;} /** * Return <code>true</code> if the scope (set of attributes) of * the enclosed request are hidden and <code>false</code> otherwise. */ public boolean getHideScope() { return this.hide_scope; } /** * Remove all local to this wrapper request attributes. */ public void clear() { attributes.clear(); } /** * Retrieve an attribute of this request. If the attribute is not found * in this wrapper and <code>hideScope</code> is false, then the parent * request object is searched. * * @param name The name of the attribute. * @return The attribute's value if found and <code>null</code> otherwise. */ public Object getAttribute(String name) { Object result = attributes.get(name); if (result == null && !hide_scope) result = super.getAttribute(name); return result; } /** * Remove an attribute from the local request scope. * * @param name The attribute's name. */ public void removeAttribute(String name) { attributes.remove(name); } /** * Set an attribute in the local request scope. * * @param name The name of the attribute. * @param value The value of the attribute. If <code>null</code>, the call will * effectively remove any existing attribute with the name. */ public void setAttribute(String name, Object value) { attributes.put(name, value); } /** * Return the names of all available request attributes. */ public Enumeration getAttributeNames() { // // When we must return the attribute names of both locally defined // attributes and attributes defined in the enclosing request scope, we // first enumerate the local ones and then all those from the enclosing // request that have not been redefined locally. Hence the anonymous // enumeration class below that checks whether a name is present in the // local map before returning it. // if (!hide_scope) return new Enumeration2( new EnumIteratorAdapter(attributes.keySet().iterator()), new Enumeration() { private Object next = null; private Enumeration se = IncludeServletRequestWrapper.super.getAttributeNames(); public boolean hasMoreElements() { if (next != null) return true; else while (se.hasMoreElements()) if (!attributes.containsKey(next = se.nextElement())) break; return next != null; } public Object nextElement() { if (hasMoreElements()) { Object result = next; next = null; return result; } else throw new NoSuchElementException( "Of attributeNames enumeration returned by IncludeServletRequestWrapper"); } }); else return new EnumIteratorAdapter(attributes.keySet().iterator()); } } The wrapper maintains its own attributes map and an additional configuration flag called hideScope that allows the programmer to choose whether attributes in the original request should be visible to the included resource. The code is straightforward except for the getAttributeNames() method, which performs some acrobatics to ensure that the names of all attributes that would be returned from a call to getAttribute() are properly enumerated. When the wrapper must expose the attributes from the enclosing scope (i.e., when hideScope is false), this method must return the names of all locally stored attributes together with the names of all remaining attributes in the parent request. The Enumeration2 class concatenates two enumerations into a single sequence. The first parameter to its constructor is an EnumIteratorAdapter, which converts an instance of a java.util.Iterator (here, an iterator over all local attribute names) to an instance of java.util.Enumeration. The second parameter of the Enumeration2 constructor is an anonymous Enumeration class that filters the attribute names returned by the wrapped request and returns only those that are not in the local attribute map. Both Enumeration2 and EnumIteratorAdapter are straightforward utility classes included in the source code download that accompanies this article.This simple class is sufficient to provide request scope calling frames from within servlet code. You only need to instantiate it with the current request object and use it as every other ServletRequest instance. To access the functionality from JSP pages in a more convenient manner, you implement your own version of the standard <jsp:include> action in the form of a small tag library consisting of three tags: include, attribute, and param. Note that you add an attribute tag to the standard include/param combination to support the passing of arbitrarily typed parameters. If you are not familiar with custom tag development, please see Resources below. Listing 2 displays the tag library descriptor (TLD). Listing 2<taglib> <tlib-version>1.0</tlib-version> <jsp-version>1.2</jsp-version> <short-name>di</short-name> <uri>/di</uri> <display-name>di</display-name> <description>A tag library for dynamic includes.</description> <tag> <name>include</name> <tag-class>IncludeTag</tag-class> <attribute> <name>page</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <type>String</type> </attribute> <attribute> <name>hideScope</name> <required>false</required> <rtexprvalue>true</rtexprvalue> <type>String</type> </attribute> </tag> <tag> <name>attribute</name> <tag-class>AttributeTag</tag-class> <attribute> <name>name</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <type>String</type> </attribute> <attribute> <name>value</name> <required>false</required> <rtexprvalue>true</rtexprvalue> <type>Object</type> </attribute> </tag> <tag> <name>param</name> <tag-class>ParamTag</tag-class> <attribute> <name>name</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <type>String</type> </attribute> <attribute> <name>value</name> <required>true</required> <rtexprvalue>true</rtexprvalue> <type>String</type> </attribute> </tag> </taglib> The attribute and param tags are very similar in that they both expect a name and a value tag attribute. The include tag expects the standard page attribute, and it supports an additional hideScope attribute for configuring the underlying request wrapper. Listing 3 shows the implementation of these tags.Listing 3public class IncludeTag extends TagSupport { private boolean hide_scope; private String page; private StringBuffer query_string = new StringBuffer(); private IncludeServletRequestWrapper request_wrapper; public int doStartTag() throws JspException { request_wrapper = new IncludeServletRequestWrapper(pageContext.getRequest()); return TagSupport.EVAL_BODY_INCLUDE; } public int doEndTag() throws JspException { if (page == null || page.length() == 0) throw new JspException("IncludeTag: page parameter is null or empty."); if (query_string.length() > 0) page += "?" + query_string; try { RequestDispatcher rdisp = request_wrapper.getRequestDispatcher(page); if (rdisp == null) throw new JspException("Coult not get request dispatcher for " + page + ", most likely the path " + page + " is wrong."); request_wrapper.setHideScope(hide_scope); rdisp.include(request_wrapper, pageContext.getResponse()); } catch (Exception ex) { throw new JspException("include tag: while trying to include resource " + page + ": " + ex.toString(), ex); } query_string.setLength(0); return TagSupport.EVAL_PAGE; } public void setAttribute(String name, Object value) { request_wrapper.setAttribute(name, value); } public void setParam(String name, String value) { query_string.append(name); query_string.append('='); query_string.append(value); } public void setPage(String page) { this.page = page; } public String getPage() { return page; } public void setHideScope(boolean hide_scope) { this.hide_scope = hide_scope; } public boolean getHideScope() { return hide_scope; } } public class AttributeTag extends TagSupport { private String name; private Object value; public int doEndTag() throws JspException { try { IncludeTag parent = (IncludeTag)getParent(); if (parent == null) throw new JspException("the 'di:attribute' can only appear " + "within an enclosing 'di:include' tag."); parent.setAttribute(name, value); return EVAL_PAGE; } catch (ClassCastException ex) { throw new JspException("the 'di:attribute' can only appear " + "within an enclosing 'di:include' tag."); } } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setValue(Object value) { this.value = value; } public Object getValue() { return value; } } The IncludeTag class provides methods for the nested attribute and param tags to store request attributes and parameters for the future include performed in the doEndTag() method. The list of request parameters is stored into a query string appended to the page URI before the dispatch. Note that the implementation AttributeTag checks that it is properly nested within an include and throws an exception when it is not. The ParamTag class is very similar to AttributeTag, omitted here for brevity. An exampleLet me first point out that the applicability of the “local request attributes” technique lies more in the context of large Web applications where many intermediate inclusions compose each request’s final response. Nevertheless, in this section I describe a short example of its use, demonstrating how it enables the natural decomposition of presentation logic into well-defined, self-contained units.Suppose you have an e-commerce site where users can browse through product listings. When detailed information about a product displays, a list of related products is also shown, and the user can click on one of the items in this list to view it and compare its features with the main product displayed. A view product request’s final response depends on whether the product is currently being compared with another product. In the first case, only details about the main product are displayed; in the second case, both the main and the related product are shown side by side for comparison. And in both cases, after basic product information is displayed, the page shows a list of user reviews and a few action commands such as “Buy” or “Add to Shopping Cart.” Thus, several different pieces of presentation logic compose the end result, and it comes in at least two variations: current product display only or display of current product together with a related one for comparison. Using your dynamic includes extension, you could handle those requirements with the following clean structure:A Product business object modeling a product.A view_product.jsp page that displays information about a given product. The view page uses a request-scoped instance of the Product bean previously created and stored under the attribute name product.A compare_products.jsp page that displays two products side by side. The page uses two request attributes of type Product called left and right.A product_reviews.jsp page that displays the reviews about the current product. Again, the current product is assumed to reside in the request scope as an attribute named product.A top-level product controller servlet that creates the necessary business objects, based on request parameters taken from the URL, and composes the response by invoking the appropriate views.The main request processing logic in the product controller servlet might look like this (I omit boilerplate code and all error checking for clarity): Product product = new Product(request.getParameter("productid")); request.setAttribute("product", product); if (request.getParameter("compare") != null) { Product compare_with = new Product(request.getParameter("compare")); IncludeServletRequestWrapper wrapper = new IncludeServletRequestWrapper(request); wrapper.setAttribute("left", product); wrapper.setAttribute("right", compare_with); request.getRequestDispatcher("compare_products.jsp").include(wrapper, response); } else request.getRequestDispatcher("view_product.jsp").include(request, response); request.getRequestDispatcher("product_reviews.jsp").include(request, response); The code creates the current product bean from the request parameter productid. It then checks for the presence of a compare request parameter indicating that the user has requested a comparison between two products. If that is the case, the compare_products.jsp page is included after the request attributes it expects—left and right—have been set. Otherwise the normal view_product.jsp is invoked. Finally, the servlet includes the product_reviews.jsp page to always display the latest reviews about the currently viewed main product.The view_product.jsp page does not care whether it displays a product standalone or in comparison with another. It simply needs a product bean in the request:<jsp:useBean scope="request" id="product" class="com.mycompany.Product"/> <table class="legacyTable"> <tr><td>Product name:</td><td><jsp:getProperty name="product" property="name"/></td></tr> <tr><td>Price:</td><td><jsp:getProperty name="product" property="price"/></td></tr> etc... </table> Similarly, the compare_product.jsp page is not concerned with the context from which it was invoked; that may be the context of comparing a main product of interest to some other related product or some other browsing context. It simply puts two views side by side, assuming the two beans to be viewed are stored under the names left and right in the request scope: <table class="legacyTable"> <tr> <td width="50%"> <di:include page="view_product.jsp"> <di:attribute name="product" value='<%= request.getAttribute("left")%>'/> </di:include> </td> <td width="50%"> <di:include page="view_product.jsp"> <di:attribute name="product" value='<%= request.getAttribute("right")%>'/> </di:include> </td> </tr> </table> Observe how the logic of the various players in the response construction is decoupled. The main servlet is not aware of the fact that the compare_product.jsp relies on the view_product.jsp for product detail display. The compare_product.jsp sets up a separate product request attribute before each view_product.jsp include without worrying about a possible side effect that could eventually disrupt the logic of pages included later in the request processing, such as the product_reviews.jsp page. In sum, the request attributes “call frame” of a dynamically included resource has enabled you to truly encapsulate pieces of presentation logic in a reusable manner and avoid the headaches associated with managing a global namespace.ImprovementsYou can exploit several variations on the request wrapper theme presented above. Here are a few suggestions:Extend the wrapper’s functionality to allow the invoked resource to modify the caller’s own request attributes.Extend the wrapper’s functionality by overriding request parameter-related methods to allow a programmer to hide the original request parameters.Extend the wrapper to allow the possibility of hiding only the caller’s attributes or only the caller’s request parameters.Or, allow the caller to expose only some of the attributes in its scope, for example by adding an exposeAttribute() method.If you have adopted the JSTL with its EL, you might add EL support in the di tag library. You could do so by using one of the Jakarta implementations (see Resources below), commons-el, or the reference JSTL implementation. The commons-el library requires a new API from the JSP 2.0 specification, which makes it impractical. Alternatively, you can rely on the JSTL implementation you have probably already deployed. In your tag library’s doStartTag() or doEndTag() methods, simply invoke the org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager‘s evaluate() method for all tag attributes and work with the returned values instead. For example, modify the AttributeTag.doEndTag() method as follows:... import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager; ... public int doEndTag() throws JspException { ... String name_evaled = EvaluationManager.evaluate("name", name, String.class, this, pageContext); String value_evaled = EvaluationManager.evaluate("value", value, Object.class, this, pageContext); parent.setAttribute(name_evaled, value_evaled); ... } Then, in the compare_products.jsp page from above you could write:<di:include page="view_product.jsp"> <di:attribute name="product" value="${left}"/> </di:include> instead of using a scriplet that accesses the request object. Note that this is not an official, standard API for evaluating EL expressions. Hence, you might consider abstracting away the EL evaluation in your own class to allow for easier future changes.Limitations and JSP 2.0 tag filesOne limitation of this approach to treating JSP pages and servlets as callable procedures is the lack of formal means to properly declare the expected parameters by a dynamic resource. You probably stumbled across this problem when you were wondering what request parameters and scoped variables a given JSP depends on. The only meaningful solution is to document those parameters and scoped attributes, for example, at the beginning of the page. Still, that would not provide a way for a JSP container to check whether a resource is included with the proper calling context. The new JSP 2.0 standard, which at the time of this writing is in the final draft stages with the Tomcat reference implementation still in beta, proposes a new presentation logic reuse mechanism based on the existing tag extension facility. In JSP 2.0, it is possible to develop tag files, which are essentially JSP pages that can be invoked using the familiar tag syntax. Tag files have well-defined syntax and semantics for passing parameters between the tag caller and the tag file itself. They embed an approach very similar to the one I have just described, with the obvious advantage of formalizing the idea, and thus allowing a JSP container to perform translation time checks on parameters. On the other hand, they have a few disadvantages: they are only applicable in a pure JSP world and are not callable standalone. Whether you will want to encapsulate presentation logic as tag files or regular JSP pages and servlets will depend on the particular case and your application’s overall design.Complete isolationIn this article, I presented a way to cleanly and completely isolate the runtime context of dynamically included server-side resources. This allows Web developers to decouple presentation logic into separate, reusable units at the JSP page/servlet level. Moreover, by encapsulating reusable presentation logic into JSP pages with a well-defined interface of scoped variables today, you are one small step from adopting JSP 2.0’s upcoming, promising tag files mechanism.Borislav Iordanov is cofounder and chief architect at Kobrix Software, where he leads the architecture and development of rapid application development (RAD) tools for Java-based, thin-client development; in particular, a framework called TICL and a visual environment called WebFaces. He has been programming with server-side Java for the past 5 years and with object-oriented software technologies for the past 10 years. Web DevelopmentJava