A Taglib example of practical use!

This is an interesting experiment where we populate placeholders in a template from the database with arbitrary values whose type and number are also defined in the database.
Before you proceed with this sample application it would be advisable to go through http://cuppajavamattiz.com/2007/05/21/web-design-patterns-at-work-with-pagination/ – for setting up a JSF app.
The basic idea is to show how a custom tablib is implemented with all its frills, but the app could be of practical use too, say in dyanmic web content management.
Before you start coding, you need to create some mysql tables which will be used by the app DAO.
Here they are for starters:

create database template;
DROP TABLE IF EXISTS template;
CREATE TABLE  template(template_id int(11) NOT NULL auto_increment,template_text varchar(512), PRIMARY KEY  (template_id));
DROP TABLE IF EXISTS template_vars;
CREATE TABLE  template_vars(template_id int(11) NOT NULL,var_id int(11) NOT NULL,var_type varchar(5) default NULL,  PRIMARY KEY  (template_id,var_id), FOREIGN KEY (template_id) REFERENCES template (template_id));
insert into template(template_text)values("Dear Mr. **$**, Dated **$** your telephone number **$** will no longer be active. Please make a note of this.");
insert into template(template_text)values("Hallo Mr. **$**, This is to inform you that you have won the grand raffle worth $ **$**. Please do the needful and collect it from our **$** office. The offer expires on **$**");
insert into template_vars values(1, 1, "A");
insert into template_vars values(1, 2, "D");
insert into template_vars values(1, 3, "N");
insert into template_vars values(2, 1,"A" );
insert into template_vars values(2, 2,"N");
insert into template_vars values(2, 3,"A");
insert into template_vars values(2, 4,"D");

I have used the following code:

A – Alphanumeric
D- Date
N – NUmeric

If you are familiar with eclipse as I am, that would make work easier. The otherwise you would have to go through the whole hog of creating a web app through the Ant build tool, of which you will have lots of examples on this site.

I made the following change to mysql-ds.xml in server/default/deploy of the Jboss server.

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
	<local-tx-datasource>
		<jndi-name>dbpool</jndi-name>
		<connection-url>jdbc:mysql://localhost:3306/TEMPLATE</connection-url>
		<driver-class>com.mysql.jdbc.Driver</driver-class>
		<user-name>admin</user-name>
		<password>admin</password>
		<metadata>
			<type-mapping>mySQL</type-mapping>
		</metadata>
	</local-tx-datasource>
</datasources>

My WAR file has the following structure:

templatePage.jsp
templatePopulation.jsp
displayPopulatedTemplate.jsp
<meta-INF>
<web-INF>
        ContentMgmtTag.tld
        faces-config.xml
        html_basic.tld (from jboss impl.)
        jsf_core.tld (from jboss impl.)
        web.xml
        <classes>
          <com>
              <mattiz>
                  <contentmgmt>
                      <dao>
                          ContentMgmtDAO.class
                      <domain>
                          ContentMgmtBean.class
                      <taglib>
                          ContentMgmtTag.class
                      <util>
                          MattizException.class
                      <vo>
                          ContentMgmtVO.class

The taglib and the tld file:

ContentMgmtTag.java

package com.mattiz.contentmgmt.taglib;
 
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;
import com.mattiz.contentmgmt.dao.ContentMgmtDAO;
import com.mattiz.contentmgmt.util.MattizException;
import com.mattiz.contentmgmt.vo.ContentMgmtVO;
 
public class ContentMgmtTag implements Tag {
    private PageContext pc = null;
    private Tag parent = null;
    private Object templateId;
 
    public void setPageContext(PageContext p) {
        pc = p;
    }
 
    public void setParent(Tag t) {
        parent = t;
    }
 
    public Tag getParent() {
        return parent;
    }
 
    public int doStartTag() throws JspException {
        try {
            ContentMgmtDAO contentMgmtDao = new ContentMgmtDAO();
            List contentMgmtVOList = contentMgmtDao.getVariableData(Integer
                    .parseInt(getTemplateId().toString()));
            Iterator cmvoli = contentMgmtVOList.iterator();
            pc.getOut().write("<table>");
            int i = 0;
            while (cmvoli.hasNext()) {
                i++;
                String param = null;
                if (pc.getRequest().getAttribute("param" + i) != null) {
                    param = (String) pc.getRequest().getAttribute("param" + i);
                }
                ContentMgmtVO contentMgmtVO = (ContentMgmtVO) cmvoli.next();
                String varType = contentMgmtVO.getVarType();
                String varTypeFull = null;
                if (varType.equals("A")) {
                    varTypeFull = "Alphanumeric";
                } else if (varType.equals("D")) {
                    varTypeFull = "Date";
                } else if (varType.equals("N")) {
                    varTypeFull = "Numeric";
                }
                pc.getOut().write("<tr><td>");
                if (pc.getRequest().getAttribute("param" + i) == null) {
                    pc.getOut()
                            .write(contentMgmtVO.getVarId()
                                    + "</td><td>   "
                                    + "<input type=\"text\" maxlength = \"10\" size=\"10\" name = \"populationParam\"/><br/>"
                                    + "</td><td>   " + varTypeFull);
                    pc.getOut().write(
                            "<input type=\"hidden\" name= \"populationParamType\" value=\""
                                    + varType + "\">");
                    pc.getOut().write("</td></tr>");
                } else {
                    pc.getOut()
                            .write(contentMgmtVO.getVarId()
                                    + "</td><td>   "
                                    + "<input type=\"text\" maxlength = \"10\" size=\"10\" name = \"populationParam\" value = \""
                                    + param + "\"/><br/>" + "</td><td>   "
                                    + varTypeFull);
                    pc.getOut().write(
                            "<input type=\"hidden\" name= \"populationParamType\" value=\""
                                    + varType + "\">");
                    pc.getOut().write("</td></tr>");
                }
 
            }
            pc.getOut().write("</table>");
            pc.getOut().write(
                    "<input type=\"hidden\" name=\"templateId\" value=\""
                            + getTemplateId().toString() + "\">");
        } catch (MattizException e) {
            throw new JspException("Error in tag", e);
        } catch (IOException e) {
            e.printStackTrace();
            throw new JspException("Error in tag", e);
        }
        return SKIP_BODY;
    }
 
    public int doEndTag() throws JspException {
        return 0;
    }
 
    public void release() {
        pc = null;
        parent = null;
    }
 
    public Object getTemplateId() {
        return templateId;
    }
 
    public void setTemplateId(Object templateId) {
        this.templateId = templateId;
    }
}

The ContentMgmtTag.tld needs to be put under WEB-INF with the other JSF taglibs

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

<taglib>
	<tlibversion>1.0</tlibversion>
	<jspversion>1.1</jspversion>
	<shortname>MattizTag</shortname>
	<uri>http://www.mattiz.com</uri>
	<info>Jsf Content Mgmt Custom Tag</info>

	<tag>
		<name>ContentMgmtTag</name>
		<tagclass>
			com.mattiz.contentmgmt.taglib.ContentMgmtTag
</tagclass>
		<bodycontent>empty</bodycontent>
		<info>Jsf Content Mgmt Custom Tag</info>

		<attribute>
			<name>templateId</name>
			<required>true</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
	</tag>
</taglib>

The jsf pages:

templatePage.jsp

<%@page contentType="text/html"%>
<%@ taglib
uri="/WEB-INF/html_basic.tld" prefix="h"%>
<%@ taglib
uri="/WEB-INF/jsf_core.tld" prefix="f"%>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
			<title>Insert title here</title>
	</head>
	<body>
		<f:view>
			<h:form id="templateForm">
				<h:selectOneListbox value="#{contentMgmtBean.selectedTemplateId}"
					id="selectedTemplate">
					<f:selectItems id="templateIdList" value="#{contentMgmtBean.templates}" />
				</h:selectOneListbox>
				<br />
				<h:commandButton value="Submit" type="submit"
					action="#{contentMgmtBean.submitTemplate}" />
			</h:form>
		</f:view>
	</body>
</html>

templatePopulation.jsp

<%@page contentType="text/html"%>
<%@ taglib
uri="/WEB-INF/html_basic.tld" prefix="h"%>
<%@ taglib
uri="/WEB-INF/jsf_core.tld" prefix="f"%>
<%@ taglib
uri="/WEB-INF/ContentMgmtTag.tld" prefix="mattiz"%>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
			<title>Insert title here</title>
	</head>
	<body>
		<f:view>
			<h:outputText value="#{contentMgmtBean.template}" />
			<br />
			<h:form id="populationForm">
				<h:message for="populationForm" id="msgForm"></h:message>
				<br />
				<mattiz:ContentMgmtTag
					templateId='<%=request.getAttribute("selectedTemplate")%>' />
				<h:commandButton value="Add" type="submit"
					action="#{contentMgmtBean.populateTemplate}" />
			</h:form>
		</f:view>
	</body>
</html>

displayPopulatedTemplate.jsp

<%@page contentType="text/html"%>
<%@ taglib
uri="/WEB-INF/html_basic.tld" prefix="h"%>
<%@ taglib
uri="/WEB-INF/jsf_core.tld" prefix="f"%>
<%@ taglib
uri="/WEB-INF/ContentMgmtTag.tld" prefix="mattiz"%>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
			<title>Insert title here</title>
	</head>
	<body>
		<f:view>
			<h:outputText value="#{contentMgmtBean.populatedTemplate}" />
			<br />
		</f:view>
	</body>
</html>

I have used a single backing bean ContentMgmtBean.java for all the jsp pages for simplicity (or does it make things more complex?)

package com.mattiz.contentmgmt.domain;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import javax.servlet.http.HttpServletRequest;
import com.mattiz.contentmgmt.dao.ContentMgmtDAO;
import com.mattiz.contentmgmt.util.MattizException;

public class ContentMgmtBean {
	FacesContext context = FacesContext.getCurrentInstance();
	HttpServletRequest request = (HttpServletRequest) context
			.getExternalContext().getRequest();
	private int selectedTemplateId = 1;
	private String populatedTemplate;

	public String populateTemplate() {
		try {
			String[] populationParamArr = request
					.getParameterValues("populationParam");
			String[] populationParamTypeArr = request
					.getParameterValues("populationParamType");
			for (int i = 0; i < populationParamArr.length; i++) {
				if (populationParamTypeArr[i].equals("N")) {
					if (!validateNumericValue(populationParamArr[i])) {
						FacesMessage aMessage = new FacesMessage(
								FacesMessage.SEVERITY_ERROR, null,
								"Invalid numeric value.");
						context.addMessage("populationForm", aMessage);
						populateParamRequestAttributes(populationParamArr);
						request.setAttribute("selectedTemplate",
								request.getParameter("templateId"));
						return "";
					}
				}
				if (populationParamTypeArr[i].equals("D")) {
					if (!validateDate(populationParamArr[i])) {
						FacesMessage aMessage = new FacesMessage(
								FacesMessage.SEVERITY_ERROR,
								"The date that you provided is invalid. Please         enter date in dd/MM/yy format.",
								null);
						context.addMessage("populationForm", aMessage);
						populateParamRequestAttributes(populationParamArr);
						request.setAttribute("selectedTemplate",
								request.getParameter("templateId"));
						return "";
					}
				}
				if (populationParamTypeArr[i].equals("A")) {
					// do nothing
				}
			}
			int templateId = Integer.parseInt(request
					.getParameter("templateId"));

			ContentMgmtDAO contentMgmtDao = new ContentMgmtDAO();
			String templateText = contentMgmtDao.getTemplate(templateId);
			for (int i = 0; i < populationParamArr.length; i++) {
				templateText = templateText.replaceFirst("\\*\\*\\$\\*\\*",
                        populationParamArr[i]);
			}

			setPopulatedTemplate(templateText);
			return "variablesPopulated";
		} catch (MattizException e) {
			FacesMessage aMessage = new FacesMessage(
					FacesMessage.SEVERITY_ERROR, null,
					"Critical Error while populating template");
			context.addMessage("templateForm", aMessage);
			return "";
		}
	}

	public List getTemplates() {
		try {
			ContentMgmtDAO contentMgmtDao = new ContentMgmtDAO();
			List templateList = contentMgmtDao.getTemplates();
			Iterator tli = templateList.iterator();
			List templateIdSelectItems = new ArrayList();
			while (tli.hasNext()) {
				String templateId = (String) tli.next();
				SelectItem selectItem = new SelectItem(templateId);
				templateIdSelectItems.add(selectItem);
			}
			return templateIdSelectItems;
		} catch (MattizException e) {
			return null;
		}
	}

	public String submitTemplate() {
		request.setAttribute("selectedTemplate", (getSelectedTemplateId() + ""));
		return "templateSelected";
	}

	public int getSelectedTemplateId() {
		return selectedTemplateId;
	}

	public void setSelectedTemplateId(int selectedTemplateId) {
		this.selectedTemplateId = selectedTemplateId;
	}

	public String getTemplate() {
		try {
			ContentMgmtDAO contentMgmtDao = new ContentMgmtDAO();
			String templateText = contentMgmtDao
					.getTemplate(getSelectedTemplateId());
			return templateText;
		} catch (MattizException e) {
			FacesMessage aMessage = new FacesMessage(
					FacesMessage.SEVERITY_ERROR, null,
					"Critical Error while getting template text");
			context.addMessage("populationForm", aMessage);
			return "";
		}
	}

	public String getPopulatedTemplate() {
		return populatedTemplate;
	}

	public void setPopulatedTemplate(String populatedTemplate) {
		this.populatedTemplate = populatedTemplate;
	}

	private boolean validateNumericValue(String numericValue) {
		String numericTemplate = "1234567890";
		for (int i = 0; i < numericValue.length(); i++) {
			if (numericTemplate.indexOf(numericValue.charAt(i)) == -1) {
				return false;
			}
		}
		return true;
	}

	private boolean validateDate(String dateParam) {
		DateFormat dateFormat = null;
		Date date = null;
		boolean flag = true;
		try {
			dateFormat = new SimpleDateFormat("dd/MM/yy");
			date = dateFormat.parse(dateParam);
			if (!dateFormat.format(date).equals(dateParam)) {
				FacesMessage msg = new FacesMessage(
						FacesMessage.SEVERITY_ERROR,
						"The date that you provided is invalid ", null);
				context.addMessage("dateId", msg);
				flag = false;
			}
		} catch (ParseException e) {
			flag = false;
		}
		return flag;
	}

	public void populateParamRequestAttributes(String[] params) {
		for (int i = 0; i < params.length; i++) {
			request.setAttribute("param" + (i + 1), params[i]);
		}
	}
}

The other java classes:

ContentMgmtVO.java the only value object

package com.mattiz.contentmgmt.vo;

public class ContentMgmtVO {
	private int varId;
	private String varType;

	public int getVarId() {
		return varId;
	}

	public void setVarId(int varId) {
		this.varId = varId;
	}

	public String getVarType() {
		return varType;
	}

	public void setVarType(String varType) {
		this.varType = varType;
	}
}

MattizException.java as usual

package com.mattiz.contentmgmt.util;

public class MattizException extends Exception {
	public MattizException() {
		super();
	}

	public MattizException(String msg, Throwable t) {
		super(msg, t);
	}
}

The DAO class that gets to the DB:

ContentMgmtDAO.java

package com.mattiz.contentmgmt.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import com.mattiz.contentmgmt.util.MattizException;
import com.mattiz.contentmgmt.vo.ContentMgmtVO;

public class ContentMgmtDAO {
	private static DataSource ds;
	private Connection conn;
	public static final String DATASOURCE_LOOKUP_NAME = "java:dbpool";

	public ContentMgmtDAO() throws MattizException {
		try {
			InitialContext ctx = null;
			ctx = new InitialContext();
			ds = (DataSource) ctx.lookup(DATASOURCE_LOOKUP_NAME);
		} catch (javax.naming.NamingException jnx) {
			throw new MattizException("Error creating datasource", jnx);
		}
	}

	public List getVariableData(int templateId) throws MattizException {
		ResultSet rs = null;
		PreparedStatement ps = null;
		List contentMgmtVOList = new ArrayList();
		try {
			conn = ds.getConnection();
			String qry = "select var_id, var_type from template_vars where template_id = ?";
			ps = conn.prepareStatement(qry);
			ps.setInt(1, templateId);
			rs = ps.executeQuery();
			while (rs.next()) {
				int varId = rs.getInt(1);
				String varType = rs.getString(2);
				ContentMgmtVO contentMgmtVO = new ContentMgmtVO();
				contentMgmtVO.setVarId(varId);
				contentMgmtVO.setVarType(varType);
				contentMgmtVOList.add(contentMgmtVO);
			}
			return contentMgmtVOList;
		} catch (SQLException e) {
			throw new MattizException("Error getting variable data", e);
		} finally {
			closeStatements(rs, ps, conn);
		}
	}

	public List getVariableTypeList(int templateId) throws MattizException {
		ResultSet rs = null;
		PreparedStatement ps = null;
		String varType = null;
		ArrayList varTypesList = new ArrayList();
		try {
			conn = ds.getConnection();
			String qry = "select var_type from template_vars where template_id = ?";
			ps = conn.prepareStatement(qry);
			ps.setInt(1, templateId);
			rs = ps.executeQuery();
			while (rs.next()) {
				varType = rs.getString(1);
				varTypesList.add(varType);
			}
			return varTypesList;
		} catch (SQLException e) {
			throw new MattizException("Error getting variable type list", e);
		} finally {
			closeStatements(rs, ps, conn);
		}
	}

	public List getTemplates() throws MattizException {
		ResultSet rs = null;
		PreparedStatement ps = null;
		int templateId = 0;
		ArrayList templateIdList = new ArrayList();
		try {
			conn = ds.getConnection();
			String qry = "select template_id from template";
			ps = conn.prepareStatement(qry);
			rs = ps.executeQuery();
			while (rs.next()) {
				templateId = rs.getInt(1);
				templateIdList.add(templateId + "");
			}
			return templateIdList;
		} catch (SQLException e) {
			throw new MattizException("Error getting template IDs", e);
		} finally {
			closeStatements(rs, ps, conn);
		}
	}

	public String getTemplate(int templateId) throws MattizException {
		ResultSet rs = null;
		PreparedStatement ps = null;
		String templateText = null;
		try {
			conn = ds.getConnection();
			String qry = "select template_text from template where template_id = ?";
			ps = conn.prepareStatement(qry);
			ps.setInt(1, templateId);
			rs = ps.executeQuery();
			while (rs.next()) {
				templateText = rs.getString(1);
			}
			return templateText;
		} catch (SQLException e) {
			throw new MattizException("Error getting template text", e);
		} finally {
			closeStatements(rs, ps, conn);
		}
	}

	private void closeStatements(ResultSet rs, PreparedStatement ps,
			Connection conn) throws MattizException {
		if (rs != null) {
			try {
				rs.close();
			} catch (Exception e) {
				throw new MattizException("Error while closing result set", e);
			}
			rs = null;
		}
		if (ps != null) {
			try {
				ps.close();
			} catch (Exception e) {
				throw new MattizException("Error while closing prepared stmt",
						e);
			}
			ps = null;
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (Exception e) {
				throw new MattizException("Error while closing connection", e);
			}
			conn = null;
		}
	}
}

faces-config.xml the heart of JSF:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
	<navigation-rule>
		<navigation-case>
			<from-outcome>variablesPopulated</from-outcome>
			<to-view-id>/displayPopulatedTemplate.jsp</to-view-id>
		</navigation-case>
	</navigation-rule>
	<navigation-rule>
		<navigation-case>
			<from-outcome>templateSelected</from-outcome>
			<to-view-id>/templatePopulation.jsp</to-view-id>
		</navigation-case>
	</navigation-rule>
	<managed-bean>
		<description>
			JSF Backing Bean for temp.jsp
</description>
		<managed-bean-name>contentMgmtBean</managed-bean-name>
		<managed-bean-class>
			com.mattiz.contentmgmt.domain.ContentMgmtBean
</managed-bean-class>
		<managed-bean-scope>request</managed-bean-scope>
	</managed-bean>
</faces-config>

web.xml with no changes:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	version="2.4"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<context-param>
		<param-name>javax.faces.CONFIG_FILES</param-name>
		<param-value>/WEB-INF/faces-config.xml</param-value>
	</context-param>
	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>0</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.faces</url-pattern>
	</servlet-mapping>
</web-app>

Actually the request attribute(selectedTemplate) passed to the mattiz:ContentMgmtTag in templatePopulation.jsp is superfluous. It can be extracted from the request object as are other request attributes done in the taglib class. But this illustrates how a run time expression can be passed dynamically to a custom tag.
Also note that you can enhance your taglib in the Taglib class to output the “value” attribute, so that the page retains the values that were previously entered. That way you can avoid writing scriptlets for this in the jsp.

The url to call->
http://localhost:8080/ContentManagement/templatePage.faces

The application in action:

one3

two2

three2

four2

five2