Developer: J2EE & Web Services

Reusability in Web Applications
by Andrei Cioroianu

Learn how to reuse Web content and Java code with the help of JSP Tag Files, JSF, and Oracle ADF Faces.

 

Downloads for this article:
 Sample code
 Oracle JDeveloper 10g (10.1.3)

Published October 2005

Code reuse is a very good way to increase developer productivity and application maintainability. Instead of reinventing the wheel, you should always look for well-designed frameworks and customizable components. Application-specific code can also be reused across modules and even across related projects. This latter type of reusability allows you to make rapid changes, exploit new features globally, and spend less time testing and debugging.

 

These things might sound like good advice for programmers, but Web developers should pay attention to them too. Many are already using frameworks such as Java Server Faces (JSF), Oracle ADF Faces, and Apache MyFaces, which provide built-in components along with support for creating additional reusable components. Very often, however, the bits of HTML and JSP markup are copied from one Web page and pasted into other pages, which means that multiplied markup has to be modified everywhere when the Web content changes. In addition, the look of an application becomes inconsistent if some of the pages aren't updated. This doesn't happen if the UI parts are reused across pages so that any change requires editing in exactly one place.

 

In this article I will offer some best practices for reusing UI parts in Web applications that are based on JSF and ADF Faces. You will learn how to create templates that define the layout of the Web pages and how to reuse forms, menus, and button bars. You'll also find out how to transform existing JSP pages in order to make them easier to maintain and how to use the components provided by JSF and Oracle ADF Faces together with JSTL and modern JSP features such as tag files.

 

Java Web Reusability Features

 

Since its first version, JSP has offered some basic mechanisms that encouraged reusability, such as the support for JavaBeans, the <%@include%> directive and the <jsp:include> tag that is based on the RequestDispatcher of the Servlets API. JSTL added the <c:import> tag, which allows you to include the content of a resource that can be located within the same application, on the same server or even on a remote server. Struts Tiles builds an entire framework around this content-inclusion feature. JSF supports it too, letting you build sub-forms that use the <f:subview> tag. JSP 2.0 added a new feature called "implicit includes." These are declared in the web.xml file with <include-prelude> and <include-coda>. As you can see, page/fragment inclusion has many variations, but each of them has its own purpose and context.

 

The support for custom tags exists since JSP 1.1, which provided an API for building tag libraries. This API was enhanced by JSP 1.2, but many considered that it was too complex. Therefore, JSP 2.0 defined a fresh API for the same purpose. The newer API for tag libraries was called Simple Tags API and the old one is now known as Classic Tags API. Many Web frameworks, such as Struts, JSF and JSTL still use the Classic Tags API so that they can be used with both JSP 1.2 and JSP 2.0. The Simple Tags API, is the foundation of Tag Files, another JSP 2.0 feature that lets you build tag libraries, using the JSP syntax. In addition to Simple Tags and Tag Files, the JSP 2.0 specification defined EL Functions that allow you to call static Java methods from your JSP pages, using the EL syntax.

 

The JSF standard defines components as its reusable units. These are much more powerful than the custom tags, but they are also harder to design and implement. You probably won't have to build your own JSF components since several companies and open source organizations are making available libraries of JSF components. The examples of this article use Oracle ADF Faces, which is the most advanced framework based on the JSF standard.

 

Creating Page Templates. All pages of a typical Web application share a common layout, which can be defined in a single place, such as a JSP tag file. This template can generate the header and body tags, the application's menu, and other sections that appear in all pages. In addition, the template can contain setup tags for loading resource bundles, setting JSP variables, and so on. It doesn't make sense to repeat this markup in every Web page of the application. In this section you'll learn how to build a custom template based on JSF and Oracle ADF Faces, using Oracle JDeveloper 10g (10.1.3) (in Early Access release at the time of this writing).

 

JDeveloper provides a wizard that lets you create JSF page templates. Select the New item of the File menu to open the New Gallery window. Then, go to the JSF category in Web Tier, select JSF JSP Template in the right panel, and click OK:

 

figure 1

 

Click Next to skip the Welcome page, then select the J2EE version that you use and click Next again:

 

figure 2

 

Provide a file name for the template:

 

figure 3

 

Choose the component binding style:

 

figure 4

 

Specify whether you want to use an error page:

 

figure 5

 

Select the tag libraries that you want to use:

 

figure 6

 

Provide a page title and other page properties:

 

figure 7

 

Click Finish. JDeveloper will create the template and will open it in a visual editor. You can use the Component Palette to add JSF and Oracle ADF Faces components to the template. Then, you can select JSF JSP from Template in the New Gallery window to create JSF pages based on the template that you've just created. This is a very easy way to build pages from templates. Another way is to move the common JSF markup into a reusable tag file. The following paragraphs explore this second option.

 

Creating the Tag File. Select the New item of the File menu to open the New Gallery window. Then, go to the JSP category in Web Tier, select JSP Tag File in the right panel, and click OK:

 

figure 8

 

JDeveloper will open a wizard window for creating the JSP tag file. Click Next to skip the Welcome page, enter pageTemplate.tag in the File Name field and click Next:

 

figure 9

 

Now you can define the attributes of the template tag. Let's say you are building a Web-based wizard and you want each page to have a step ID and a title. The tag file needs this information to customize the template markup for each page. Click the Add button, enter the step attribute name, and set Required to true. Do the same for the other attribute named title:

 

figure 10

 

Click Next and Finish. JDeveloper creates the pageTemplate.tag file in the tags subdirectory of the WEB-INF directory. The two tag attributes are defined with the <%@attribute%> directive:
<%@ attribute name="step" required="true" %>
<%@ attribute name="title" required="true" %>
After creating the tag file, JDeveloper opens it for editing. The following paragraphs explain the template code of the sample application that is presented throughout this article.

 

Using JSF and Oracle ADF Faces in Tag Files. Whether you want to use these frameworks within a tag file of within a regular page, you have to declare them with the <%@taglib%> directive. Select JSP in the Component Palette and then click Taglib. A dialog box lets you enter the URI and prefix of the tag library that you want to use:

 

figure 11

 

You can also use the Component Palette to drop any tag onto a JSP page and if its <%@taglib%> directive is not on the page, JDeveloper will add it automatically. The pageTemplate.tag example uses four tag libraries: JSTL Core, JSF Core, Oracle ADF Faces Core and Oracle ADF Faces HTML:
<%@ taglib prefix="af" 
    uri="http://xmlns.oracle.com/adf/faces" %>
<%@ taglib prefix="afh"
    uri="http://xmlns.oracle.com/adf/faces/html" %>
There is one important thing that you should always keep in mind when using JSF and JSTL together, or when using JSF in tag files: The JSF framework doesn't support the page scope, which is the default JSP scope of the JSTL tags. Therefore, you should explicitly specify the request scope for the JSP variables that are used with the tags of JSTL, JSF and Oracle ADF Faces. In addition, all attributes of a tag file are accessible through JSP variables maintained in the page scope. You have to copy the attributes within the request scope so that they can be used with the JSF and Oracle ADF Faces tags:
<c:set var="pageTitle" scope="request" value="${pageScope.title}"/>
You can also store the values of the attributes within beans managed by JSF. Let's say you have a class named WizardBean that is configured as a managed bean in faces-config.xml:
<faces-config>
    ...
    <managed-bean>
        <managed-bean-name>wizard</managed-bean-name>
        <managed-bean-class>webreuse.WizardBean</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
    </managed-bean>
    ...
</faces-config>
The example bean has a currentStep property where you could store the value of the step attribute. This can be done with the <c:set> tag of JSTL, but you must make sure that the bean exists in the session scope (as declared in the faces-config.xml file). JSF creates the managed bean instance only when it is referred in a JSF EL expression for the first time. If the JSTL tag is executed before the JSF or Oracle ADF Faces tags that access the managed bean, you should use <jsp:useBean> to ensure the presence of the bean instance in the session scope. After that, you can safely use the <c:set> tag:
<jsp:useBean class="webreuse.WizardBean" 
    id="wizard" scope="session"/>
<c:set target="${wizard}" property="currentStep" 
    value="${pageScope.step}"/>
The above code can be entered manually or you can use JDeveloper's Component Palette. Select JSP and then click UseBean to add the <jsp:useBean> tag. JDeveloper opens a dialog box where you must provide the attributes of the tag:

 

figure 12

 

Now you can finally add the template code in the pageTemplate.tag file:
<f:view>
    <afh:html>
        <afh:head title="#{pageTitle}"/>
        <afh:body>
            <af:panelPage title="#{pageTitle}">
                <jsp:doBody/>
            </af:panelPage>
        </afh:body>
    </afh:html>
</f:view>
All JSF and Oracle ADF Faces components must be placed inside <f:view>. The <afh:html>, <afh:head>, and <afh:body> components produce the HTML tags with the same names. The <af:panelPage> component renders the page title. Then, <jsp:doBody> invokes the JSP content that you placed between <tags:pageTemplate> and </tags:pageTemplate> in the JSP pages that use the template tag. Such a JSP page would look like this:
<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>

<tags:pageTemplate step="..." title="...">
    ... JSP content executed by doBody ...
</tags:pageTemplate>
When this page is executed, the tag file generates the <html>, <head>, and <body> tags along with the page title and other header tags. Then, the template tag file invokes the JSP content wrapped between <tags:pageTemplate> and </tags:pageTemplate>. After that, the tag file may generate a footer and closes the page with </body> and </html>. The next section uses the template tag in the pages of the Web-based wizard.

 

Reusing Forms, Menus, and Other UI Parts

 

UI panels constructed with the JSF and Oracle ADF Faces components can be reused in multiple pages, using either "subviews" or JSP tag files. The former solution is compatible with the old JSP 1.2 version, but tag files are more flexible. This section shows how to split a Web form into multiple pages and tag files based on JSF and Oracle ADF Faces. When a user accesses the application for the first time, he will go through those pages step by step, using a wizard-style interface with Back and Next buttons. At a later time, he might want to update the information that he provided initially. In this case, the user needs direct access to the application's pages, using a menu-based interface.

 

The same Web forms can be used in both scenarios described above. In addition, all forms can be combined in one confirmation page that lets the user review the information in read-only mode. When you have to change one of the forms, you'll edit a single file. Each modification will appear in the wizard-style interface (used to obtain the information from the user), in the confirmation page (used to review the information), and in the menu-based interface (used by the user to update the information). Without reusing the forms, you would have to make the same changes in three different places.

 

Developing the Backing Bean. You've already found out in the previous section that the example application uses a backing bean named WizardBean. The pageTemplate.tag file sets the currentStep property, which maintains the step ID of the current form that the user sees in his browser. The example application has three forms followed by the confirmation page (fourth step), whose ID is defined by the MAX_STEP constant. This constant is exposed as a bean property named maxStep so that it can be accessed in Web pages, using the JSF EL:
package webreuse;

public class WizardBean implements java.io.Serializable {
    public final static int MAX_STEP = 4;
    private int currentStep;
    private String connName, connType;
    private String userName, password, role;
    private String driver, hostName, sid;
    private int jdbcPort;

    public int getMaxStep() {
        return MAX_STEP;
    }

    public int getCurrentStep() {
        return currentStep;
    }

    public void setCurrentStep(int currentStep) {
        this.currentStep = currentStep;
    }

    ...
}
In addition to currentStep and maxStep, the WizardBean class has several other properties that maintain the wizard parameters provided by the user: connName, connType, userName, password, role, driver, hostName, sid, and jdbcPort. The example application does nothing with the user data, but a real-world wizard would use it to configure a database connection. The WizardBean also implements several JSF actions, which are executed when the user clicks the wizard's buttons. These methods are presented later in this section.

 

To create your own beans, you can select New in the File menu to open the New Gallery window. Then, go to the Simple Files category in General, select Java Class in the right panel, and click OK:

 

figure 13

 

Provide the class name and the package name. Then, click OK to create the class:

 

figure 14

 

After declaring a field, such as private int currentStep, right click its name and select Generate Accessors. Click OK to generate the get and set methods:

 

figure 15

 

Creating Reusable Forms. The wizard's main forms are coded as reusable tag files. The first form (form1.tag) contains a text field and a drop-down list:
<!-- form1.tag -->

<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>

<af:inputText id="connName" required="true" columns="40"
        label="Connection Name: " value="#{wizard.connName}"
        readOnly="#{wizard.currentStep == wizard.maxStep}"/>

<af:selectOneChoice id="connType" required="true"
        label="Connection Type: " value="#{wizard.connType}"
        readOnly="#{wizard.currentStep == wizard.maxStep}">
    <af:selectItem label="Oracle (JDBC)" value="Oracle_JDBC"/>
</af:selectOneChoice>
The second form (form2.tag) contains three text fields:
<!-- form2.tag -->

<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>

<af:inputText id="userName" required="true" columns="40"
        label="User Name: " value="#{wizard.userName}"
        readOnly="#{wizard.currentStep == wizard.maxStep}"/>

<af:inputText id="password" required="true" columns="40"
        label="Password: " value="#{wizard.password}" secret="true"
        readOnly="#{wizard.currentStep == wizard.maxStep}"/>

<af:inputText id="role" required="false" columns="40"
        label="Role: " value="#{wizard.role}"
        readOnly="#{wizard.currentStep == wizard.maxStep}"/>
The third form (form3.tag) is similar to the other two:
<!-- form3.tag -->

<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>

<af:selectOneChoice id="driver" required="true"
        label="Driver: " value="#{wizard.driver}"
        readOnly="#{wizard.currentStep == wizard.maxStep}">
    <af:selectItem label="thin" value="thin"/>
    <af:selectItem label="oci8" value="oci8"/>
</af:selectOneChoice>

<af:inputText id="hostName" required="true" columns="40"
        label="Host Name: " value="#{wizard.hostName}"
        readOnly="#{wizard.currentStep == wizard.maxStep}"/>

<af:inputText id="sid" required="true" columns="40"
        label="SID: " value="#{wizard.sid}"
        readOnly="#{wizard.currentStep == wizard.maxStep}"/>

<af:inputText id="jdbcPort" required="true" columns="40"
        label="JDBC Port: " value="#{wizard.jdbcPort}"
        readOnly="#{wizard.currentStep == wizard.maxStep}"/>
As you've probably noticed, none of the three tag files contains markup for arranging the Oracle ADF Faces components. The absence of any layout tag allows you to use the form tags individually or to combine them within the confirmation page. Oracle ADF Faces provides a powerful component named <af:panelForm> that does the layout automatically. In addition to the main components, a form usually contains other tags, such as <h:messages globalOnly="true"/> and <af:objectLegend name="required"/>. All these can be grouped in a tag file named formTemplate.tag:
<!-- formTemplate.tag -->

<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>

<h:panelGrid columns="1" border="0" cellspacing="5">
    <h:messages globalOnly="true"/>
    <af:objectLegend name="required"/>
    <af:panelForm>
        <jsp:doBody/>
    </af:panelForm>
</h:panelGrid>
A JSF page that uses pageTemplate.tag and formTemplate.tag looks like this:
<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>

<tags:pageTemplate step="..." title="...">
        <af:form id="...">
                ...
                <tags:formTemplate>
                        <tags:form123/>
                </tags:formTemplate>
                ...
        </af:form>
</tags:pageTemplate>
In the preceding code snippet, <tags:form123/>represents any of the three main forms of the wizard (& lt;tags:form1/>, <tags:form2/>, or <tags:form3/>). Besides the components of these forms, <af:form> may contain other JSF and Oracle ADF Faces components, such as buttons. The following paragraph describe the application's pages that use <tags:pageTemplate>, <tags:formTemplate>, <tags:form1>, <tags:form2>, and <tags:form3>. Those concrete examples show the real benefits of building JSF user interfaces with reusable tag files.

 

Wizard-style Interface. The pages of the Web-based wizard will have buttons labeled Back, Next and Finish. These buttons can be defined in a tag file named stepButtons.tag:
<!-- stepButtons.tag -->

<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>

<af:panelButtonBar>

    <af:singleStepButtonBar
        selectedStep="#{wizard.currentStep}"
        maxStep="#{wizard.maxStep}"
        previousAction="#{wizard.previousAction}"
        nextAction="#{wizard.nextAction}"/>

    <c:if test="${wizard.currentStep == wizard.maxStep}">
        <af:commandButton text="Finish"
            action="#{wizard.finishAction}"/>
    </c:if>

</af:panelButtonBar>
The WizardBean class contains the action methods that are executed when the user clicks the buttons:
package webreuse;

public class WizardBean implements java.io.Serializable {
    ...

    public String previousAction() {
        if (currentStep <= 1)
            return null;
        else {
            currentStep--;
            return "step" + currentStep;
        }
    }

    public String nextAction() {
        if (currentStep >= getMaxStep())
            return null;
        else {
            currentStep++;
            return "step" + currentStep;
        }
    }

    public String finishAction() {
        currentStep = 0;
        return "finished";
    }

    ...
}
The outcomes returned by the action methods are used in the navigation rules of the faces-config.xml file:
<faces-config>
    ...
    <navigation-rule>
        <from-view-id>*</from-view-id>
        <navigation-case>
            <from-outcome>step1</from-outcome>
            <to-view-id>/step1.jsp</to-view-id>
        </navigation-case>
    </navigation-rule>
    ...
    <navigation-rule>
        <from-view-id>*</from-view-id>
        <navigation-case>
            <from-outcome>step4</from-outcome>
            <to-view-id>/confirm.jsp</to-view-id>
        </navigation-case>
    </navigation-rule>

    <navigation-rule>
        <from-view-id>*</from-view-id>
        <navigation-case>
            <from-outcome>finished</from-outcome>
            <to-view-id>/index.jsp</to-view-id>
        </navigation-case>
    </navigation-rule>
    ...
</faces-config>
In addition to all visible components, the wizard pages should contain a hidden field that is bound to the currentStep property of WizardBean. You've seen that pageTemplate.tag sets this property every time a page is executed. A user, however, might click the Back button of his browser. As a result of this action, the current step viewed in his browser won't coincide with the value of the currentStep property because the browser retrieves the pages from his cache instead of requesting the execution of the JSF pages.

 

This doesn't cause any problems if the currentStep value is submitted along with the form data every time the user clicks a button. The hiddenData.tag file contains currentStep as a hidden field. In addition, this tag file generates hidden fields for all bean properties if currentStep is equal to maxStep, which means that the tag is used in the confirmation page:
<!-- hiddenData.tag -->

<h:inputHidden id="currentStep" value="#{wizard.currentStep}"/>

<c:if test="${wizard.currentStep == wizard.maxStep}">
    <h:inputHidden id="h_connName" value="#{wizard.connName}"/>
    <h:inputHidden id="h_connType" value="#{wizard.connType}"/>
    <h:inputHidden id="h_userName" value="#{wizard.userName}"/>
    <h:inputHidden id="h_password" value="#{wizard.password}"/>
    <h:inputHidden id="h_role" value="#{wizard.role}"/>
    <h:inputHidden id="h_driver" value="#{wizard.driver}"/>
    <h:inputHidden id="h_hostName" value="#{wizard.hostName}"/>
    <h:inputHidden id="h_sid" value="#{wizard.sid}"/>
    <h:inputHidden id="h_jdbcPort" value="#{wizard.jdbcPort}"/>
</c:if>
All wizard pieces can be assembled in JSF pages, such as step1.jsp:
<!-- step1.jsp -->

<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>

<tags:pageTemplate step="1" 
        title="Create Database Connection - Step 1">
    <af:form id="form1">
        <tags:formTemplate>
            <tags:form1/>
        </tags:formTemplate>
        <tags:stepButtons/>
        <tags:hiddenData/>
    </af:form>
</tags:pageTemplate>
The step2.jsp and step3.jsp pages are very similar to step1.jsp. As an exercise, you could try building a template for these pages, reducing each of them to four lines of code. The confirm.jsp page shows all three forms together, but the components work in read-only mode, taking less screen space and requiring no user interaction:
<!-- confirm.jsp -->

<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>

<tags:pageTemplate step="4" title="Confirm Connection Parameters">
    <af:form id="form4">
        <tags:formTemplate>
            <tags:form1/>
            <tags:form2/>
            <tags:form3/>
        </tags:formTemplate>
        <tags:stepButtons/>
        <tags:hiddenData/>
    </af:form>
</tags:pageTemplate>
Menu-based Interface. Let's suppose a user went through the wizard's pages step by step, providing all required information. If the user needs to modify something at a later time, he should not have to go through all wizard pages again. Instead, the user interface should let him go directly to the form where the information must be modified. The menuTabs.tag file uses the Oracle ADF Faces component with the same name to build the menu:
<!-- menuTabs.tag -->

<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>

<af:menuTabs>
    <af:commandMenuItem text="Name and Type" action="tab1"
        selected="#{wizard.currentStep == 1}"/>
    <af:commandMenuItem text="Authentication" action="tab2"
        selected="#{wizard.currentStep == 2}"/>
    <af:commandMenuItem text="Connection" action="tab3"
        selected="#{wizard.currentStep == 3}"/>
</af:menuTabs>
The menu's navigation rules are defined in faces-config.xml:
<faces-config>
    ...
    <navigation-rule>
        <from-view-id>*</from-view-id>
        <navigation-case>
            <from-outcome>tab1</from-outcome>
            <to-view-id>/tab1.jsp</to-view-id>
        </navigation-case>
    </navigation-rule>
    ...
    <navigation-rule>
        <from-view-id>*</from-view-id>
        <navigation-case>
            <from-outcome>tab3</from-outcome>
            <to-view-id>/tab3.jsp</to-view-id>
        </navigation-case>
    </navigation-rule>
    ...
</faces-config>
When the user clicks a tab of the menu, the form data is submitted to the Web server where the JSF framework updates the backing bean. If the user wants to update a form without changing the current tab, he needs a submit button. The submitButton.tag file provides it:
<!-- submitButton.tag -->

<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>

<af:commandButton id="command" text="Submit" action="submitAction"/>
The tab1.jsp, tab2.jsp, and tab3.jsp files attach the menu to the wizard's forms. Here is tab1.jsp:
<!-- tab1.jsp -->

<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %>
<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>

<tags:pageTemplate step="1" title="Edit Database Connection">
    <af:form id="form1">
        <tags:menuTabs/>
        <tags:formTemplate>
            <tags:form1/>
        </tags:formTemplate>
        <tags:submitButton/>
        <tags:hiddenData/>
    </af:form>
</tags:pageTemplate>
Making Presentation Logic Reusable

Everything is great if you have to build new pages from scratch using JSF and Oracle ADF Faces. But what about the old JSP pages that contain presentation logic in the form of Java code? It is hard to maintain these pages and the presentation code cannot be reused without moving it out of the JSP pages. Besides, Java code and HTML markup shouldn't be mixed in the same page because this doesn't allow Java developers and Web designers to work easily in parallel.

 

JSF and Oracle ADF Faces components solve this problem in many cases since the Web pages are built with the tags provided by these two frameworks while the Java code is placed into backing beans. JSTL and other tag libraries are also very helpful, but sometimes you just have to use Java code for generating content dynamically.

 

A good solution is to build tag libraries, using the Simple Tags API provided by JSP 2.0. This API lets you develop tag handler classes, but you must maintain a separate XML file called Tag Library Descriptor (TLD), which defines the tag names, their attributes, and so on. If this sounds too complicated you can simply move the Java code into tag files that use the JSP syntax, letting the application server generate the tag handler classes and the TLD files. Let's take a look at a JSP page named oldCode.jsp, which mixes Java and HTML. This page reads a text file whose path is given as a request parameter and displays the file's content with line numbers. It can be useful when you build a demo application and you want to show your code.

 

Important Note! Do not use the examples of this section (oldCode.jsp and newCode.jsp) in a production environment since they can reveal the source code of your application.

 

The oldCode.jsp page reads the text file, using the java.io APIs. For each line of text, it creates two variables in the JSP page scope named lineText and lineNo. The line number is formatted with the <fmt:formatNumber> tag of JSTL and the text is displayed with the <c:out> tag:
<!-- oldCode.jsp -->

<%@ page import="java.io.*" %>

<HTML>
<HEAD>
    <TITLE>${param.path}</TITLE>
</HEAD>
<BODY>

<c:if test="${empty param.path}">
    <P>The <CODE>path</CODE> parameter wasn't specified.
</c:if>

<c:if test="${!empty param.path}">
    <P><B><CODE>${param.path}</CODE></B>
<%
    String path = application.getRealPath(
        request.getParameter("path"));
    BufferedReader in = new BufferedReader(new FileReader(path));
    try {
        int lineNo = 0;
        String lineText;
        while ((lineText = in.readLine()) != null) {
            lineNo++;
            pageContext.setAttribute("lineText", lineText);
            pageContext.setAttribute("lineNo",
                new Integer(lineNo));
%>
    <fmt:formatNumber var="fmtLineNo"
        value="${lineNo}" minIntegerDigits="3"/>
    <PRE>${fmtLineNo}  <c:out value="${lineText}"/></PRE>
<%
        }
    } finally {
        in.close();
    }
%>
</c:if>

</BODY>
</HTML>
The entire Java code from the previous page example can be moved into a tag file named readTextFile.tag. There are very few changes to be made: you have to use the <%@tag%> directive and the jspContext implicit object instead of <%@page%> and pageContext. You also have to declare attributes and variables, using JSP directives:
<!-- readTextFile.tag -->

<%@ tag import="java.io.*" %>
<%@ attribute name="path" required="true" %>
<%@ variable name-given="lineText" scope="NESTED" %>
<%@ variable name-given="lineNo" scope="NESTED" %>
<%
    String path = application.getRealPath(
        (String) jspContext.getAttribute("path"));
    BufferedReader in = new BufferedReader(new FileReader(path));
    try {
        int lineNo = 0;
        String lineText;
        while ((lineText = in.readLine()) != null) {
            lineNo++;
            jspContext.setAttribute("lineText", lineText);
            jspContext.setAttribute("lineNo",
                new Integer(lineNo));
%>
            <jsp:doBody/>
<%
        }
    } finally {
        in.close();
    }
%>
You can use the readTextFile.tag file in any JSP page that needs to process a text file line by line. Each page can do anything it wants with the text lines since the tag file uses <jsp:doBody/>, allowing the JSP page to place the code that processes the current line between <tags:readTextFile> and </tags:readTextFile>. The newCode.jsp page does the same thing as the old-style example, but without mixing Java and HTML:
<!-- newCode.jsp -->

<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>

<HTML>
<HEAD>
    <TITLE>${param.path}</TITLE>
</HEAD>
<BODY>

<c:if test="${empty param.path}">
    <P>The <CODE>path</CODE> parameter wasn't specified.
</c:if>

<c:if test="${!empty param.path}">
    <P><B><CODE>${param.path}</CODE></B>
    <tags:readTextFile path="${param.path}">
        <fmt:formatNumber var="fmtLineNo"
            value="${lineNo}" minIntegerDigits="3"/>
        <PRE>${fmtLineNo}  <c:out value="${lineText}"/></PRE>
    </tags:readTextFile>
</c:if>

</BODY>
</HTML>
As you can see, it's not very hard to modify the old JSP pages so that the code can be reused. The maintenance also becomes much easier.

Reuse at Will

 

Duplicating code or content is one of the worst practices. It can be an easy solution sometimes, but changing the application becomes a lot more difficult, which means that in the long term, you can't adapt to new user requirements or fix problems quickly. Ideally, an application shouldn't contain multiple versions of the same code and the Web content shouldn't be copied and pasted.

 


Andrei Cioroianu [ devtools@devsphere.com] is the founder of Devsphere, a provider of development, integration, and consulting services. Cioroianu has written many Java articles published by Oracle Technology Network, ONJava, JavaWorld, and Java Developer's Journal. He also coauthored the books Java XML Programmer's Reference and Professional Java XML (both from Wrox Press).

Send us your comments