URL of the article:

Why Use Open Source JSP Tag Libraries?
Filling the Gaps in Monolithic Java Frameworks
by Alan M Berg
Struts and Java Server Faces are extremely popular frameworks that take away common coding chores, especially in a typical Model View Control application. This article discusses how to enhance the value of using such frameworks by the addition of open source JSP tag libraries. I have placed emphasis on the effective coupling of the tags mentioned with the JSF framework.

Introduction
Good design practice dictates that business logic is well separated from the view in any given web application. In big development shops there are enough well trained employees for specialisation within the various layers, an interaction designer for the JSP pages, and hard-core Java programmers for the backend. However, emphasizing JSP coding is advantageous for rapid design cycles, which is especially attractive for smaller organizations or businesses that have fewer resources. You do not need to reinstall the whole application when you change code in a page. You can immediately see changes. Besides, if you choose the correct tag libraries, you can hide away much of the complexity. Diminishing complexity lowers the entry-level knowledge required for the developer to be thoroughly productive. If the tab library encapsulates the essence of a problem domain, then we do not sacrifice readability. Further, the learning curve to produce a given result is lower than that required for coding the logic ourselves.

This article and follow up articles will explore a range of open source tag libraries that can help in enhancing current frameworks or deskill the creation of specific system interactions.

Myfaces
Java Server Faces (JSF) is an example of a well-respected framework. JSF [based on the concept of reusable web components] is relatively lightweight and is easier to learn than Struts. However JSF still maintains a solid grip on navigation flow and the management of beans during the lifecycle of the application. Implementations of JSF such as the ones from Oracle, IBM or Myfaces [from the Open Source community] contain enhanced render kits with extra JSP tags. Configuration of the development environment, such as Jdeveloper, allows the drag and dropping of web components within the preview panel of the IDE. Myfaces has components for menus, tabs, calendars, and enhancing the view for master detail records. Further, validators of form input include credit card and e mail checks and enhanced use of regular expressions.

In the sandbox of this community effort, sit trendy new AJAX tools: Input suggestions, auto update of data tables, as well as a scheduler. This range of components deliberately caters to the most commonly requested application features, like filling in of database records. However, once you move outside the scope, you will need a few extras to cope with user expectations. An example of stepping past the norm is a basic data warehousing tool I wrote last year. See screen grabs 1a, 1b - application logic control is via Myfaces and the enhanced charting by the CEWOLF tag library.

The rest of this article will be spent describing the charting, and image manipulation that was required for the warehousing application.


Figure 1a: Screen grab of an XY chart as rendered by the CEWOLF tag library [Courtesy: Central Computer Services at the University of Amsterdam]


Figure 1b: Screen grab of a bar chart as rendered by the CEWOLF tag

The Jakarta Tag Libraries
The Apache movement can be subdivided into a number of projects supported by like minded open source developers, all trying to engineer high quality products. Myfaces is one of their projects. Jakarta taglib is another project that contains a range of tag libraries, which act as potential enhancements. The image tag library still sits in the taglib project sandbox. This means the tag library is going through a rapid development phase where the tag definitions may change. I would recommend looking at this project for your solutions, before expanding your search further.

The image tag library wraps underlying Java image libraries and the poor man's imaging wrapper to expose functionality through a few simple definitions. CEWOLF does the same for the Xfreechart library. As one can expect with image manipulation, the process at times has the potential to be CPU intensive, especially in a public facing web application. However, the limitation is not significant for low demand intranet applications.

In general, installing a tag library involves three steps:
  1. Place the tag library definition under WEB-INF. For the image library that is the file, taglibs-image.tld
  2. Place the required supporting libraries under WEB-INF/lib. For the image library, the required jars for version 1.1 are taglibs-image.jar and pniw.jar.
  3. Mention the existence of the tag library in the relevant JSP page. For example, <%@ taglib uri="http://jakarta.apache.org/taglibs/image-1.0" prefix="img" %>

Listing 1 describes a JSP page containing an example image tag. The tag takes a copy of a template image, adds text, and saves the image to cache as a file and to the browser as a stream.

Note: The JSP page removes the cache to aid in development, otherwise; we will not get an updated image whenever we change the property values within the tag.

Listing 1: JSP Page Containing Image Tab and Supporting Code

<%@ page contentType="text/html; charset=iso-8859-1"
isErrorPage="false" errorPage="errorPage.jsp"
import="java.io.File" %>
<%@ taglib uri="http://jakarta.apache.org/taglibs/image-1.0" prefix="img" %>
<%

//Defined properties that may change overtime.
//Use for example managed JSF beans.
String message="HELLO WORLD";
int size=26;
String myDir="generated";
String myFile="image.jpg";

//Get absolute path and delete cached image
//When happy with the image comment out and allow cache to do it's job.
String realPath = this.getServletConfig().getServletContext().getRealPath("/" )+myDir+System.getProperty("file.separator")+myFile;
File file = new File(realPath);
file.delete();
%>
<html>
<head>
<title>Image tag Example</title>
<link href="cewolf.css" rel="stylesheet" type="text/css">
</head>
<body>
<h2>Hello World Example</h2>
<address>File Location:</address><code><%= realPath %></code>
<p>

<img:image src="/images/splash.jpg"
dir="<%=myDir%>"
name="<%=myFile%>"
attributes="border='0' alt='<%=message%>'">

<img:text text="<%=message%>"
x="10%"
y="75%"
bold="true"
size="<%=size%>"
/>
</img:image>
<p>
<a href="index.html">BACK</a>
</body>
</html>


The defined image tag properties were:
  • src points to the template image splash.jp
  • dir defines the directory location of the final cached image
  • name is the name of the saved final image
  • text is the message to be placed in the image
  • size is the dimensions of the text in the image

<img:image src="/images/splash.jpg"
dir="<%=myDir%>"
name="<%=myFile%>"
attributes="border='0' alt='<%=message%>'">

<img:text text="<%=message%>"
x="10%"
y="75%"
bold="true"
size="<%=size%>"
/>
</img:image>


The dir, name, and text properties depend on the print statements of string members defined earlier:

String message="HELLO WORLD";
int size=26;
String myDir="generated";
String myFile="image.jpg";


Removing the cached image is straightforward and does not rely on absolute path names:

String realPath = this.getServletConfig().getServletContext().getRealPath("/" )+myDir+System.getProperty("file.separator")+myFile;
File file = new File(realPath);
file.delete();

Plumbing JSF Managed Beans into a JSP
The last section saw us exposing the properties of the image tag (such as the message property) to Java code with out.println statements (<%= %>). This means that, if necessary, we can suck a bean into the page and use the beans properties to define tag values. As you will see later, this feature is helpful for integration with other frameworks.

JSF manages navigation and beans through a Servlet. Management of the Servlet is through an XML configuration file. Whenever one of the JSF tags in a viewed JSP mentions a managed bean, it is created on-demand. To force bean creation for use with the image or other tag libraries, use a JSF hidden input tag as described in Listing 2. The bean is then available within the scope mentioned in the JSF configuration file; in the case of this example, it is generated with scope request.

Listing 2: JSP Forcing the Creation of a Managed Bean

<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<f:view>

<h:inputHidden value="#{ImageConfigurationBean.someProperty}"/>
<%
//Bean now available as a request attribute for manipulation.
out.println("Bean: "+request.getAttribute("ImageConfigurationBean"));
%>

</f:view>


Now that we have the bean available, we can use it to pass parameters to the tag libraries. This allows JSF to manage the beans and inject information or services into the tags via <%=bean.getProperty()%>.

The CEWOLF Tag Library
The CEWOLF tag library allows for the creation of various types of graph including bar, pie, scatter, and XY charts. CEWOLF wraps the Xfreechart API. The tag libraries accept the dataset class, a custom class that implements certain interfaces expected by the JSP tag. The methods defined help modify the tab library behavior; for example, the time an image is cached will be displayed on mouse rollover in a tool tip.

Note: I have deliberately chosen the CEWOLF tutorial as a basis the code for this example. I would recommend at this point that you skim read the tutorial (see Resources section) to build background knowledge. The change in the example [from the tutorial] is the addition of getters and setters to push information into the dataset generation class.

Listing 3: The CEWOLF dataset Producing Class

import java.io.Serializable;
import java.util.Date;
import java.util.Map;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import de.laures.cewolf.DatasetProduceException;
import de.laures.cewolf.DatasetProducer;
import de.laures.cewolf.links.CategoryItemLinkGenerator;
import de.laures.cewolf.tooltips.CategoryToolTipGenerator;

public class ProduceSeries implements DatasetProducer, CategoryToolTipGenerator, CategoryItemLinkGenerator, Serializable {

//Data to pass
private String[] categories;
private String[] seriesNames;
private int CacheTimeSeconds;

public int getCacheTimeSeconds() {
return CacheTimeSeconds;
}

public void setCacheTimeSeconds(int cacheTimeSeconds) {
CacheTimeSeconds = cacheTimeSeconds;
}

public ProduceSeries(){
this.CacheTimeSeconds=0;
this.categories=new String[] {};
this.seriesNames=new String[] {};
}

public String[] getCategories() {
return categories;
}

public void setCategories(String[] categories) {
this.categories = categories;
}

public String[] getSeriesNames() {
return seriesNames;
}

public void setSeriesNames(String[] seriesNames) {
this.seriesNames = seriesNames;
}

/**
*Produces some random data.
*/
public Object produceDataset(Map params) throws DatasetProduceException {

DefaultCategoryDataset dataset = new DefaultCategoryDataset(){
/**
* @see java.lang.Object#finalize()
*/
protected void finalize() throws Throwable {
super.finalize();

}
};
for (int series = 0; series < seriesNames.length; series ++) {
int lastY = (int)(Math.random() * 1000 + 1000);
for (int i = 0; i < categories.length; i++) {
final int y = lastY + (int)(Math.random() * 200 - 100);
lastY = y;
dataset.addValue(y, seriesNames[series], categories[i]);
}
}
return dataset;
}

/**
* This producer's data is invalidated after CacheTimeSeconds seconds. By this method the
* producer can influence Cewolf's caching behaviour the way it wants to.
*/
public boolean hasExpired(Map params, Date since) {
return (System.currentTimeMillis() - since.getTime()) > this.CacheTimeSeconds*1000;
}

/**
* Returns a unique ID for this DatasetProducer
*/
public String getProducerId() {
return "Example CEWOLF";
}

/**
* Returns a link target for a special data item.
*/
public String generateLink(Object data, int series, Object category) {
return seriesNames[series];
}

protected void finalize() throws Throwable {
super.finalize();
}

public String generateToolTip(CategoryDataset arg0, int series, int arg2) {
return seriesNames[series];
}
}


The produceDataset method is the main personalization in the code. The private members, which are passed from the JSP, are read by the method to produce an instance of the dataset class. By refactoring this code further, we could even pass a class, which does the calculations for generating the dataset, to this pseudo bean. If we define an interface for this new class type, we can plug and play with different generated data distributions, without modifying this class.

Most graphs have their datasets produced from information contained in a database. We can now use this methodology to inject the database manager into the class, via a setter, and use that to read in the raw data. JSF knows to manage bean initialization; so it is possible to instance the database manager though this facility. Giving the bean application scope allows all sessions access to the database services contained within the beans getters

Listing 4 is the JSP that contains the CEWOLF tag for an XY chart. The producer class is instanced for each request. (You can also place parts of the class in synchronized blocks and then give the class a session scope lowering overall system effort.)

Listing 4: The JSP Page with CEWOLF Tag that Consumes the Dataset

<%@page contentType="text/html"
import="playground.amberg.cewolf.ProduceSeries"
%><%@taglib uri='/WEB-INF/cewolf.tld' prefix='cewolf' %>
<%
//Properties Exposed in JSP should be in managed beans.
String title = "Bean Example";
String xaxislabel = "Month";
String yaxislabel = "Units sold";
ProduceSeries seriesProducer = new ProduceSeries();
String[] categories= {"Jan","Feb","March","April"};
String[] series = {"cars","houses","boats","computers"};
seriesProducer.setCategories(categories);
seriesProducer.setSeriesNames(series);
//No caching for development.
seriesProducer.setCacheTimeSeconds(0);
request.setAttribute("getViews",seriesProducer);
%>
<HTML>
<HEAD>
<TITLE>Based on Cewolf Tutorial - Step 1</TITLE>
<link href="cewolf.css" rel="stylesheet" type="text/css">
</HEAD>
<BODY bgcolor="#DDE8F2">
<H1>Based on the Cewolf Tutorial</H1>
<H2>Exposing Cewolf to a beans influence</H2>
<cewolf:chart
id="line"
title="<%=title%>"
type="line"
xaxislabel="<%=xaxislabel%>"
yaxislabel="<%=yaxislabel%>">
<cewolf:data>
<cewolf:producer id="getViews"/>
</cewolf:data>
</cewolf:chart>
<p>
<cewolf:img chartid="line" renderer="/cewolf" width="400" height="300">
<cewolf:map linkgeneratorid="getViews" tooltipgeneratorid="getViews"/>
</cewolf:img>
<P>
<a href="index.html">BACK</a>
</BODY>
</HTML>

Injecting Services into Managed Beans
By injecting services into managed beans, we can glue the information obtained from databases, XML files or other data sources, with the tag libraries. This section will drill down further into this approach.

The dataset producer has bean like properties. The managed beans facility in JSF levers this. Listing 5 is an example of initializing the String array. This admittedly is not very interesting, as you would need to restart the web applications each time you wanted to change configuration.

Listing 5: Initializing a Managed-bean

<managed-bean>
<managed-bean-name>TestCewolf</managed-bean-name>0
<managed-bean-class>
playground.amberg.cewolf.ProduceSeries</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>seriesNames</property-name>
<list-entries>
<value>Car</value>
<value>House</value>
<value>Boat</value>
</list-entries>
</managed-property>
</managed-bean>


Listing 6 shows how to initialize a request scope bean from an application scope bean. In the currently described situation, a String[] is passed. However, if you place your persistence services in the application-scoped bean, then you can pass a data service, database connection or any other service, to the request scoped bean. For the data warehousing application mentioned previously, I used the popular SQL maps framework for persistence management and passed a singleton instance of the SQLMapClient as a property. (See the Resources section for more details.) This leaves the dataset producer free to call the database services, safe in the knowledge of thread safety and shielded from the subtleties of initialization.

Listing 6: Initializing a Request-Scoped Managed Bean from an Application-Scope Bean

<managed-bean>
<managed-bean-name>DataManager</managed-bean-name>
<managed-bean-class>playground.amberg.beans.DataManager
</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>TestCewolf</managed-bean-name>
<managed-bean-class>
playground.amberg.cewolf.ProduceSeries</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>seriesNames</property-name>
<list-entries>
<value>#{DataManager.series}</value>
</list-entries>
</managed-property>
</managed-bean>


The <list-entry> entity works for both string arrays and lists. Further, notice how easy it is to pass property values using the EFL statement #{DataManager.series}. Note that, for this approach to work with JSF, it is necessary that DataManager is a bean which has the getSeries() and setSeries() methods, as well as an empty constructor; otherwise, reflection will fail and the expression will fail to be passed correctly by the JSF Servlet.

Summary
Open source tag libraries can enhance monolithic frameworks such as JSF or struts. This article showed how the CEWOLF and image libraries could work in combination with the JSF framework and points out tactics for good fellowship.

Coming Next
In the next article, I will discuss how to use tags to build digital dashboards that collect information from numerous distributed sources. Instead of the plumbing with well-known frameworks [as was the case in this article], in the next edition I will place more emphasis on the details of the tag libraries.

Alan Berg, Bsc. MSc. MSc. PGCE, has been a lead developer at the Central Computer Services at the University of Amsterdam for the last seven years. In his spare time, he writes computer articles. He has a degree, two masters and a teaching qualification. In previous incarnations, he was a technical writer, an Internet/Linux course writer, and a science teacher. He likes to get his hands dirty with the building and gluing of systems. He remains agile by playing computer games with his kids who (sadly) consistently beat him.

Acknowledgments
I wish to express my gratefulness to my father for his motivational positivism and the CEWOLF project for clear examples and an excellent tag library.

Resources

© 2004 Software & Support Verlag GmbH. Reproduction has to be permitted by the publisher. Questions?