HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

JavaServer Pages (JSP), an API layer that extends the servlet architect, provide developers with a standard for creating template-based HTML applications. The JSP specification marries scripting tags and Java code in an HTML template with nonvisual JavaBeans and servlets running in the JSP/servlet engine. It provides a flexible solution that caters to Web developers who use a scripting approach with display logic embedded in a Web page and application developers who prefer to separate the HTML from the controlling logic by using a component-driven approach.

This article shows how to build JSP applications using a component-based framework. By leveraging nonvisual JavaBean components, the Java event model and a bean-aware IDE, I'll demonstrate how you can realize many of the rapid development advantages of component development without having to use an application server-specific IDE and API.

HTML, Application Servers and JSP
Before JSP, application server vendors created their own server-side API to display HTML Web pages. Using products such as Sun NetDynamics, SilverStream and Bluestone Web/Sapphire, developers can create a containment hierarchy of controls, set properties via property sheets and bind the controls to a database. Tags corresponding to their controls are then inserted into the HTML and used during the rendering process. Vendors developed some productive approaches to this process (they also provided input to the JSP specification); however, each vendor developed a different approach. This is now a problem since the productivity resulted from using the vendor's own API, which, of course, ran only on that vendor's server. The approach discussed in this article isn't very different from the vendors'. However, we're using commonly available tools and leveraging existing standards to improve productivity while creating a portable framework across JSP implementations.

Application server vendors are now beginning to support JSP - a good development. The products already provide most of the enterprise services needed by the JSP presentation layer. Yet base-level support of JSP differs from productive JSP support. In many of these products productive support in the form of IDE enhancements and API extensions is six months to a year away. In the meantime, you can start leveraging JSP productivity today while the vendors work toward more comprehensive solutions that differentiate their products.

Before We Begin
I'll now show what you can currently build using the tools you already own (or that are readily available). The code is limited in scope, functionality and error checking. In other words, don't try this in production!

First we'll build a JSP framework, which will consist of several JavaBeans and base classes for our application. Next we'll create a small Web application (one page) that uses the framework and beans to build the display of a JSP page. To further limit the scope of the code in demoland, we won't connect to a database or an EJB (Enterprise JavaBean) even though we would in the real world. For now we'll focus on the display aspects of a page. Most (though not all) of the code is given at the end of this article. The complete source is located on SYS-CON's Web site at www.JavaDevelopersJournal.com.

Using Components with JSP
Our JavaBeans components will encapsulate display aspects of HTML or XML tags. These tag components will reside on a JavaBeans palette in your IDE just like visual Swing JavaBeans components. Our beans will comprise HTML tags such as an input button, input text or just static HTML text. The framework is extendible, so all types of data-driven, text-based objects can be built. Does using a component-based approach sound vaguely familiar? It should. Although developing component-based JSP applications is similar to developing client-side JavaBean applications, there are several major differences. One example is event flow. In applets and applications, user actions can occur in varying sequences. In JSP/HTML applications the set of options is more defined; once the user chooses an action, the display process is a controlled event flow or batch process as the display of the page follows a predictable order of events. On demand, our tag components will "render" themselves using their properties and data. Our tags will also fire Java events during the rendering process to provide "user exits" for developers to add code that dynamically changes the runtime display. This provides a flexible architecture that the developer can exploit for Web pages with complex display logic and business rules.

JSP Scopes and the Framework
An important part of JSP and servlet development is knowing the thread safety rules for each JSP scope. JavaBeans can be instantiated in different scopes ranging from page scope (local to a page rendering) to application scope (global to all JSPs and servlets in an application). The JSPs in this article create page scope beans. This means bean instance variables are okay because the bean itself is local to a single page rendering. While page scope eliminates thread-safety concerns, heavy use of page-scope objects also creates more overhead and drags down performance.

Fortunately, the JSP framework developer can choose one of two approaches when using a framework such as this in a wider scope without making life harder for the JSP application developer. The first approach is object pooling of the beans. Application servers such as Sun NetDynamics use this technique extensively to limit the creation of new objects while serving a large number of concurrent users. The concept is the same as database connection pooling in which an instance is checked out, utilized, then checked back into the pool. The second approach separates the framework into two sets of classes: lighter-weight property objects created in a page scope that contain variable data specific to each page, and corresponding application scope objects that contain design-time property settings and methods that operate on the data. Again, our objects will be page scope objects so these measures aren't needed but I did want to address this important issue.

First Up, Events and Listeners
First we'll define the events the DataTag bean can fire. For example, the bean could fire a preDataFetch event to allow a developer to set some defaults before data is received. In another case a preHtmlOutput event that enables a developer to change the completed tag before display could be fired, or he or she could decide not to display if the current user has inadequate security. Since we're working with JavaBean components, many IDEs will recognize the events and provide visual support for easily adding them to our source code. This speeds development while providing structure to our process. For this article we'll define one event, formatOutputProperties, that fires when we're ready to add formatting to our display value. For example, we may want to format a date or add a currency format to our value. See Listing 1 for the event and listener code.

Creating a DataAdapter Class
Next, we'll define a DataAdapter class. This wrapper class retrieves values from an instance (or member) field and provides the value to the DataTag. The DataAdapter concept can become quite flexible, and subclasses could support access to JDBC resultsets and EJB components. For now we'll keep our class simple and support only single-value and array objects that use the toString() method to convert their values to string format. Wrapping an object is completed by simply passing it via the appropriate constructor. The adapter then returns values as requested, using getValue() methods. The method signatures are shown below. See Listing 2 for the complete source.

Public DataAdapter(
Object value)
Public DataAdapter(
Object[] value)
Public Object getValue()
Public Object getValue(
int Index)

Adding the DataTag Class
Now we're ready to define the DataTag. This is the core class in the framework. It controls the process of rendering text by retrieving data from a source and firing events the developer uses to customize the display process. We'll create the class and then extend it to render HTML Input Button and Text tags along with static HTML text. Our framework users will see the extensions to this object as components that are residing on the palette as JavaBeans.

Our base bean tag has two primary methods and several important properties: (1) display(int index) drives the tag-rendering process, firing events as needed - we implement this in the DataTag; (2) render(int index) is an abstract polymorphic method implemented in our subclasses - this method is called during the display process and creates and returns the HTML. Our class also contains several instance variables (see Figure 1):

Figure 1
Figure 1:

  • String jspName: Name applied to a form tag for "name" and servlet request parameter purposes
  • DataAdapter valueSource: Source object from which the tag's data value will be retrieved
  • String defaultStringValue: Value used when a tag isn't bound to a source
  • String stringValue: Temporary value changed on each rendering by retrieving either a data value from the source or a copy of the default value; developer formats this value in the formatOutputProperties event
  • String extraAttr: Free-form field used by the developer to add JavaScript or other scripting information - appended as last property
  • Vector displayListeners: Event source support
Other properties work as well. For example, an extraAttributes tag, extraAttr, is included for adding JavaScript to our tags. All of our variables that have accessor methods conforming to the JavaBean specification (or defined in BeanInfo) can be set in the bean's property sheet. However, the stringValue property is meant solely for use via the API. No problem. We can choose to hide it from the property sheet by creating a BeanInfo class. The source code for the key instance variables and methods in DataTag is located in Listing 3.

Now let's examine the display method in closer detail. This is where the real action takes place. It executes property get and set statements and fires DataTag defined events. As mentioned earlier, it makes sense to define events that fire at different points in the display process to allow runtime customization. For example, the snippet of the display logic below checks for the existence of a DataAdapter as a source of our value. If none is found, it uses a default string value provided via a property sheet.

This is another example where we could consider defining an event prior to executing this logic. Why? On a tag-by-tag basis, we could then listen for the event and execute logic to change the default value or change the source DataAdapter based on user-specific information. Let's say we're including news headlines on our JSP page. One user may prefer Wall Street Journal headlines while another prefers Newsweek. We could swap the source at runtime before the value is retrieved without changing the core display process.

if(getValueSource() == null) {
setStringValue(
getDefaultStringValue());
} else {
setStringValue(
getValueSource().
getValue(index).toString());
}

Once we set our string value for display, we execute our own event, formatOutputProperties. To utilize the event, all our framework users need to do is set up a listener and add the business logic. Since most IDEs provide event and listener code-generation support, it's a snap to implement.

With our DataTag base class coded, we can now extend the class to create other tags. Only a single method, render(int index), must be implemented, though many tags will add additional properties. For example, an HTML text tag requires size and maxlength properties. The HtmlTextTag demonstrates this in Listing 4 (property getters and setters have been eliminated to save space). The complete source and the source for other tags used in the application can be found at www.JavaDevelopersJournal.com.

Finishing Our Framework
The last class to create in our framework is the container class for the DataTags. We'll call this class PageBean because it'll be created based on a reference in a JSP page (see Listing 5 for the complete source). The bean will be the connector between the JSP page and our DataTags. We could also use it to pass JSP variables, such as the pageContext, to our DataTags. The PageBean maintains a hashtable of DataTags to the JSP page. When a developer codes one of the following tags in the JSP template,

<%=pageBean.display(
"costCenter")%>
<%=pageBean.display(i,
"costCenter")%>

the method on our PageBean directs the call to the correct DataTag using the following code:

return ((DataTag) tagTable
.get(name)).display(index);

With our lightweight framework complete, we can deploy it and build the JSP application. After we JAR the classes and install them as components in our JavaBean palette, the role of the framework developer is complete. We can now leverage our IDE's capabilities to utilize our framework to speed development of the JSP.

Creating the JSP Application
For this example we'll create a budget maintenance page to list departments and their annual budgets (see Figure 2). First we create the BudgetPageBean class to extend the PageBean class (see Listing 6). Next, we use our IDE (I'm using VisualCafé) to drag and drop DataTags representing HTML tags onto the BudgetPageBean.

Figure 2
Figure 2:

I did this for each HTML tag on the page and then renamed the tags and set the properties. For example, two of our tags are a cost center text field and an OK button (see Figure 3). We may be able to drag and drop the objects to our bean using our editor. If not, we can do it by adding them to the source as instance variables. We'll also code several set methods to initialize properties. Again, an IDE may create this code for us via property sheet changes or we'll code the changes ourselves in a constructor or init event.

Figure 3
Figure 3:

costCenterTag.setJSPName(
"costCenter");
costCenterTag.setMaxLength(8);
costCenterTag.setSize(8);
okTag.setJSPName("okButton");
okTag.setDefaultStringValue(
"OK");
okTag.setExtraAttr(
"onClick=\"return
buttonEvent(\'Ok\')\"");

Next, we'll add our instance variables that are our source data items. These values will be set by our business logic prior to displaying the page. Note: We include support for both single-value items and array items. This structure corresponds to the servlet API's support for both types via getParameter() and getParameterValues().

With all our variables defined, we'll complete the initial setup by linking the tag to its container and data source. enableTag() adds the tag to the pageBean's hashtable and setValueSource() sets the object from which we'll retrieve data values.

enableTag(costCenterTag);
costCenterTag.setValueSource(
costCenter);

Let's make this more interesting by adding currency formatting to the budget text field for display purposes. We'll just register an event listener with the budgetTag so we can respond to the formatOutputProperties event (see Figure 4). Your IDE may even add all the plumbing for the listener. In your listener, add the business logic to make the change. The code that changes the formatting is shown below.

Figure 4
Figure 4:

DataTag tag = (DataTag)
event.getSource();
tag.setStringValue("$" +
tag.getStringValue() + ".00");

Creating the JSP Page
The last step in our process is to create the JSP page. Actually, this can be your first step, depending on your preferred approach. We'll create a basic JSP page with some single-value tags along with an HTML table. Note that very little business logic or display code is needed in the JSP page. This keeps our HTML clean and provides for easier graphical HTML editing via DreamWeaver or FrontPage. The controlling display logic is maintained in our PageBean-derived class instead of being interspersed with HTML in the JSP template. The JSP source is in Listing 7. Take note that we have easily incorporated an HTML table into our source. Since our framework supports arrays, we just pass a row number to our source.

<%for(int i=0; i<3; i++) {%>
<tr><td><%=pageBean.display(
i,"dept")%></td>
<td><%=pageBean.display(
i,"budget")%></td></tr>
<%}%>

There you have it! We've implemented a JSP component-based framework and built our first Web page by applying it. Using this approach isolates the HTML template from the dynamic display control logic, allows us to use a component-based approach and leverages the rapid application capabilities of today's IDEs.

Author Bio
David Lyons is a technology director with Virtualogic in Bethesda, Maryland. A Sun NetDynamics certified instructor, David has developed Web applications using several Java application servers.
He can be reached at: dlyons@virtualogic.com.

	

Listing 1: 

package jspf; 
public class DisplayEvent extends 
    java.util.EventObject { 
  private int rowIndex; 
  
  // pass rowIndex so tag can retrive 
  // multivalued items 
  public DisplayEvent(Object source, 
      int rowIndex) { 
    super(source); 
    this.rowIndex = rowIndex; 
  } 
  public int getRowIndex() { 
    return rowIndex; 
  } 
} 

package jspf; 
public interface DisplayListener 
    extends java.util.EventListener { 
  public void formatOutputProperties( 
      DisplayEvent e); 
} 

Listing 2: 
  
package jspf; 
public class DataAdapter extends Object 
    implements java.io.Serializable { 
  private Object    value; 
  private Object[]  arrayValue; 
  private boolean   multivalued = false; 
  
  // wrap a single-value item 
  public DataAdapter(Object value) { 
    this.value = value; 
    multivalued = false; 
  } 
  // wrap an array item 
  public DataAdapter(Object[] value) { 
    this.arrayValue = value; 
    multivalued = true; 
  } 
  public Object getValue() { 
    if(multivalued) { 
      return arrayValue[0]; 
    } else { 
      return value; 
    } 
  } 

 // return the requested value. Note: 
  // works with both single- and multi- 
  // value items 
  public Object getValue(int index) { 
   if(multivalued) { 
      return arrayValue[index]; 
    } else { 
      return value; 
    } 
  } 
} 

Listing 3: 
  
  private String jspName = ""; 
  private DataAdapter valueSource; 
  private String defaultStringValue = ""; 
  private String stringValue = ""; 
  private String extraAttr = ""; 
  private Vector displayListeners = new 
  Vector(); 

  abstract public String render(int index); 
  
  protected String display(int index) { 
    // reset our temp value before display 
    setStringValue(""); 
    // get default value or get dynamic value 
    // from our data source 
    if(getValueSource() == null) { 
      setStringValue(getDefaultString- 
      Value()); 
    } else { 
      setStringValue(getValueSource(). 
          getValue(index).toString()); 
    } 
    // fire format event so developers can 
    // customize stringValue to display 
    Vector v1 = (Vector)displayListen- 
    ers.clone(); 
    for(int i=0; i < v1.size(); i++) 
      ((DisplayListener) v1.elementAt(i)). 
          formatOutputProperties( 
          new DisplayEvent(this, index)); 
    // create html by calling tag's 
    // render process 
    return render(index); 
  } 

Listing 4: 
  
package jspf; 
public class HtmlTextTag extends DataTag { 
  private int maxLength = 10; 
  private int size = 10; 
  private boolean password = false; 

  public String render(int index) { 
    StringBuffer  html = new String- 
    Buffer(); 

    html.append("<INPUT TYPE="); 
    // check for password type 
    if(isPassword()) 
      html.append("PASSWORD "); 
    else 
      html.append("TEXT "); 
    html.append("NAME=").append("\""). 
        append(getJSPName()).append("\"  "); 
    html.append("VALUE=").append("\""). 
        append(getStringValue()).append("\" "); 
    html.append("MAXLENGTH=\""). 
        append(getMaxLength()).append("\" "); 
    html.append("SIZE=\"").append(getSize()). 
        append("\""); 
    // if extra attributes included, 
    // append them. 
    if(! getExtraAttr().equals("")) 
     html.append(" ").append(getExtraAttr()); 
    html.append(">"); 
    return html.toString(); 
  } 
  // property get / set methods excluded 
  // from listing  see complete source 
  

Listing 5: 
  
package jspf; 
import java.util.Hashtable; 
  
public abstract class PageBean 
    implements java.io.Serializable { 
  private Hashtable tagTable = new Hashtable(); 

  // single value display process 
  public String display(String name) { 
    return processDisplay(-1, name); 
  } 
  // overloaded display process multi- 
  // value items 
  public String display(int index, 
  String name) { 
    return processDisplay(index, name); 
  } 
  // forward display request to named tag 
  protected String processDisplay(int index, 
      String name) { 
    return ((DataTag) tagTable.get(name)). 
          display(index); 
  } 
  // add tag to hashtable of tags we 
  // can display 
  protected synchronized void enableTag( 
      DataTag tag) { 
    tagTable.put(tag.getJSPName(), tag); 
  } 
} 

Listing 6: 
  
package jspf; 

public class BudgetPageBean extends jspf.PageBean{ 
  String costCenter = "Value to   replace"; 
  String[] dept = {"Acct", "Mgmt", "Mkt"}; 
  Integer[] budget = new Integer[3]; 

  public BudgetPageBean() { 
    // visual cafe init - varies per IDE 
    vcInit(); 
    // load budget array 
    budget[0] = new Integer(100); 
    budget[1] = new Integer(120); 
    budget[2] = new Integer(130); 
    // load tags we'll display from JSP 
    enableTag(costCenterTag); 
    enableTag(deptTag); 
    enableTag(budgetTag); 
    enableTag(okTag); 
    enableTag(cancelTag); 
    // set sourceValues 
    costCenterTag.setValueSource(cost- 
    Center); 
    deptTag.setValueSource(dept); 
    budgetTag.setValueSource(budget); 
  
    SymDisplay lSymDisplay = new 
    SymDisplay(); 
     budgetTag.addDisplayListen- 
     er(lSymDisplay); 
  
   costCenterTag.addDisplayListener(lSymDis play); 
  } 
  
  public void vcInit() { 
    costCenterTag.setJSPName("costCenter"); 
    costCenterTag.setMaxLength(8); 
    costCenterTag.setSize(8); 
    deptTag.setJSPName("dept"); 
    budgetTag.setJSPName("budget"); 
    okTag.setExtraAttr( 
      "onClick=\"return 
       buttonEvent(\'Ok\')\""); 
    okTag.setDefaultStringValue("OK"); 
    okTag.setJSPName("okButton"); 
    cancelTag.setExtraAttr( 
    "onClick=\"return buttonEvent(\'Can- 
     cel\')\""); 
    cancelTag.setDefaultStringValue("Cancel"); 
    cancelTag.setJSPName("cancelButton"); 
  } 
  HtmlTextTag costCenterTag = new Html- 
  TextTag(); 
  HtmlStaticTextTag deptTag = new 
    HtmlStaticTextTag(); 
  HtmlTextTag budgetTag = new HtmlText Tag(); 
  HtmlButtonTag okTag = new HtmlButtonTag(); 
  HtmlButtonTag cancelTag = new Html- 
  ButtonTag(); 

  // diplaylistener class added by IDE 
  class SymDisplay implements Dis- 
  playListener { 
    public void formatOutputProperties( 
        DisplayEvent event) { 
      Object object = event.get- 
      Source(); 
      if (object == budgetTag) 
        budgetTag_formatOutputProper- 
        ties(event); 
  else if (object == costCenterTag) costCenterTag_formatOutputProperties(event); 
    } 
  } 
  // add budget currency formatting 
  void budgetTag_formatOutputProperties( 
      DisplayEvent event) { 
    DataTag tag = (DataTag) event.get- 
    Source(); 
    tag.setStringValue("$" + tag.getStringValue() 
        + ".00"); 
  } 
  // set cost center value on the fly 
  // via code 
  void costCenterTag_formatOutputProperties( 
      jspf.DisplayEvent event) { 
    DataTag tag = (DataTag) event.get- 
    Source(); 
    tag.setStringValue("A12345"); 
  } 
} 

Listing 7: 
  
<%@page language="java" session="true"%> 
<jsp:useBean id="pageBean" scope="page" 
 class="jspf.BudgetPageBean"/> 
<html><head><title>JSP Budget Maintenance</title> 
<SCRIPT LANGUAGE="JavaScript"> 
  // sample Javascript function we call 
  // via setting extraAttr property 
  function buttonEvent(button) { 
    alert("I clicked the " + button + " 
    button!"); 
    return false; 
  } 
</SCRIPT> 
</head><body bgcolor="#FFFFFF"> 
<h2>JSP Budget Maintenance</h2> 
<form method="post" action="tp.jsp" name="frm1"> 
<p>Cost Center: 
  <%=pageBean.display("costCenter")%></p> 
  <table border="1"> 
    <tr><td>Department</td><td>Budget</td></tr> 
    <%for(int i = 0; i < 3; i++) {%> 
    <tr> 
      <td><%=pageBean.display(i,"dept")%></td> 
      <td><%=pageBean.display(i,"budget")%></td> 
    </tr> 
    <%}%> 
  </table> 
<p><%=pageBean.display("okButton")%> 
<%=pageBean.display("cancelButton")%></p> 
</form></body></html> 



Download Assoicated Source Files (Zip format - 21 KB)
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: info@sys-con.com

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.