JSP - Custom Tag Development

In this article, lets discuss about custom tag development in java based web application.

Custom tags can be created by the developer when, the tags provided by JSTL or any MVC framework like (Struts, Spring) are not sufficient enough to satisfy a particular requirement. Please create custom tags if it is absolutely necessary and not for sake. Developer should be aware that he is not reinventing the wheel by creating new custom tags.

Now let's create a simple custom tag for creating and populating a drop down list in a JSP. Have a look at the following TLD file

techdive.tld

<?xml version="1.0" encoding="UTF-8"?>
  <taglib>
    <tlibversion>1.1</tlibversion>
        <jspversion>2.0</jspversion>
    <shortname>options</shortname>
    <info>Tag library for common operations</info>
    <tag>
      <name>options</name>
      <tagclass>in.techdive.customTag.OptionsTag</tagclass>
      <attribute>
        <name>beanName</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
      </attribute>
   
      <attribute>
        <name>methodName</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
      </attribute>

      <attribute>
        <name>selectName</name>
        <required>false</required>
        <rtexprvalue>true</rtexprvalue>
      </attribute>

      <attribute>
        <name>onChange</name>
        <required>false</required>
        <rtexprvalue>true</rtexprvalue>
      </attribute>
     
    </tag>
  </taglib>

It consists of four attributes ,
1. beanName – className of the class which contains method to populate the drop down list.
2. methodName – method Name specified in the above class which is to be called to get data for dropdown list.
3. selectName – name to be used for the dropdown list (or select tag in html).
4. onChange – attribute value for html onchange event , usually a javascript method call.

Now let's create the implementation class to make the tag work at runtime.

OptionsTag.java

/**
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE IS DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package in.techdive.customTag;

import java.lang.reflect.Method;
import java.util.ArrayList;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;

public class OptionsTag extends BodyTagSupport
{

        private String  onChange        = null;

        private String  selectName      = null;

        private String  methodName      = null;

        public String getOnChange()
        {
                return onChange;
        }

        public void setOnChange(String onChange)
        {
                this.onChange = onChange;
        }

        public String getSelectName()
        {
                return selectName;
        }

        public void setSelectName(String selectName)
        {
                this.selectName = selectName;
        }

        public String getMethodName()
        {
                return methodName;
        }

        public void setMethodName(String methodName)
        {
                this.methodName = methodName;
        }

        private String  beanName        = null;

        public String getBeanName()
        {
                return beanName;
        }

        public void setBeanName(String beanName)
        {
                this.beanName = beanName;
        }

        public OptionsTag()
        {
        }

        public int doStartTag() throws JspException
        {
                HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
                ArrayList indicesList = new ArrayList();

                try
                {
                        Class className = Class.forName(beanName);
                        Object[] objArr = new Object[0];

                        Class[] arg = new Class[0];

                        Method getMethod = className.getMethod(methodName, arg);

                        KeyValuePair dto = null;
                        StringBuffer htmlString = new StringBuffer();

                        htmlString.append("<SELECT style='width:10%' NAME=");
                        htmlString.append("'");
                        htmlString.append(selectName);
                        htmlString.append("' ");
                        if (onChange != null)
                        {
                                htmlString.append(" onchange='");
                                htmlString.append(onChange);
                                htmlString.append("' ");
                        }
                        htmlString.append(">");

                        Object[] objArr1 = new Object[0];

                        indicesList = (ArrayList<KeyValuePair>) getMethod.invoke(className.newInstance(), objArr1);

                        int indicesListSize = indicesList.size();

                        htmlString.append("<OPTION value=''>Select...</OPTION>");

                        for (int i = 0; i < indicesListSize; i++)
                        {
                                dto = (KeyValuePair) indicesList.get(i);
                                htmlString.append("<OPTION value='");
                                htmlString.append(String.valueOf(dto.getKey()));
                                htmlString.append("'");

                                htmlString.append(">");
                                htmlString.append(dto.getValue());
                                htmlString.append("</option>");
                        }

                        htmlString.append("</SELECT>");
                        JspWriter writer = pageContext.getOut();
                        writer.print(htmlString.toString());
                }
                catch (Exception e)
                {
                        System.out.println("Exception in OptionsTag -> " + e);
                }
                return EVAL_BODY_BUFFERED;
        }
}

The above class OptionsTag.java contains all the attributes present in techdive.tld file as properties with getter and setter methods. It extends from base class BodyTagSupport and overrides the method doStartTag(), which will be called when the custom tag is executed by the Servlet container at runtime. This method actually creates an instance specified (as full class name) in the property beanName and invokes the method specified in the methodName property in the object of the class. This method should return a list of KeyValuePair objects , whose key/values are used to populate the dropdownlist in JSP. A sample class which contains method to return a list of KeyValuePair objects is shown below.

OptionsBean.java

import java.util.ArrayList;

public class OptionsBean
{
        public ArrayList<KeyValuePair> getDataForOptions()
        {
                ArrayList<KeyValuePair> collection = new ArrayList<KeyValuePair>();

                KeyValuePair kVP1 = new KeyValuePair("ind", "INDIA");
                KeyValuePair kVP2 = new KeyValuePair("eng", "ENGLAND");
                KeyValuePair kVP3 = new KeyValuePair("esp", "SPAIN");
                KeyValuePair kVP4 = new KeyValuePair("rsa", "SOUTH AFRICA");

                collection.add(kVP1);
                collection.add(kVP2);
                collection.add(kVP3);
                collection.add(kVP4);

                return collection;
        }
}

public class KeyValuePair
{
        private String  key       = null;

        private String  value   = null;

        public String getKey()
        {
                return key;
        }

        public void setKey(String key)
        {
                this.key = key;
        }

        public String getValue()
        {
                return value;
        }

        public void setValue(String value)
        {
                this.value = value;
        }

        public KeyValuePair(String key, String value)
        {
                super();
                this.key = key;
                this.value = value;
        }
}

Have a look at the below JSP file which uses our custom tag.

techDive_custom_tag.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
  pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core"  prefix="c" %>
<%@ taglib uri="WEB-INF/tld/techdive.tld"  prefix="tdd" %>
<html>
<head>
<title>Auto Login Form</title>
</head>

<body>
    <form method="post" action="<c:url value="postDataReceiverServlet"/>">
        <tr>

            <td>

                   <label name="countries">Countries</label>
             </td>

            <td>

                <tdd:options  beanName="in.techdive.customTag.OptionsBean"
                selectName="techDiveOptions" methodName="getDataForOptions"/>

            </td>

            </tr>
    </form>
</body>

</html>

We need to copy the techdive.tld file inside WEB-INF/tld folder. The same file should be referenced in the JSP using the taglib directive. We have given the prefix name as "tdd" and it is used to reference the tag "options" as specified in the TLD.

In this way we can create our own custom tags to suite our application requirements.

Technology: 

Search