Call JavaBean methods from JSP 2.0 pages

Develop custom tags with dynamic attributes

The new JavaServer Pages (JSP) version incorporates the expression language (EL) introduced by the JSP Standard Tag Library (JSTL) to let Web designers produce scriptless JSP pages that don't contain Java code. Since JSP 2.0 provides backward compatibility to JSP 1.x, you may still include Java snippets in your pages, but tag handlers and JavaBean components are much better places for Java-based functionality.

JSP 2.0 provides new features for tag handlers such as dynamic attributes, the Simple Invocation Protocol, and .tag files. You still use the old JSP 1.0 standard actions for creating JavaBean instances and setting their properties, but now you can access bean properties, request parameters, and JSP attributes/variables with the new expression language.

All those JSP technology improvements let you achieve the goal of separating the JSP/HTML markup from the Java code. One thing is missing, however. JSP 2.0 has no syntax for calling a public nonstatic JavaBean method from a scriptless JSP page. This article solves that issue by providing a JSP 2.0 simple tag with dynamic attributes.

Note: You can download this article's source code from Resources.

Expression language needed

Suppose you have a java.util.List instance you must present as an HTML list. Here is a quick solution based on JSP 1.x:

<UL>
<%
    Iterator iterator = list.iterator();
    while (iterator.hasNext()) {
        Object elem = iterator.next();
        if (elem == null)
            elem = "";
%>
        <LI> <%= elem %> </LI>
<%
    }
%>
</UL>

Existing JSP-based Web applications consist of Java code mixed with HTML markup like the above code fragment. Maintaining hundreds of pages like that can be a nightmare if you have separate Java development and Web design teams. The solution is to move the Java code into tag libraries so that developers can do their jobs without pasting Java code within Webpages and designers can edit their Webpages without worrying about breaking the Java code.

However, JSP 1.x has several problems that don't let you easily develop scriptless JSP pages. Until recently, no standard method existed for accessing Java objects from a JSP page without using Java code. In addition, coding tag handler classes wasn't as simple as it could have been.

The following lines of code are based on JSTL 1.0, which can be used with JSP 1.2. The <c:forEach> tag iterates over the elements of the given list and exports the elem variable for each element. Instead of declaring elem as a local variable, the <c:forEach> tag creates a page attribute with pageContext.setAttribute(). This attribute's value is printed with JSTL's <c:out> tag:

<UL>
<c:forEach var="elem" items="${list}">
    <LI> <c:out value="${elem}"/> </LI>
</c:forEach> 
</UL>

JSTL provides standard tags for processing XML documents and accessing relational databases along with formatting tags, internationalization tags, conditional tags, iterator tags, URL-related tags, and other general-purpose tags. JSTL has solved many of JSP 1.x's problems with the help of an expression language that allows you to access Java objects from JSP pages without using Java code. For example, instead of looking for an attribute or accessing a request parameter with:

<%= pageContext.findAttribute("a") %>
<%= request.getParameter("p") %>

you may now use:

${a}
${param.p}

You can access the JSP page context objects, page/request/session/ application attributes (also known as JSP variables), JavaBean properties, collection elements, request parameters, initialization parameters, cookies, and HTTP headers.

With JSP 1.2, the expression language is available only to JSTL-based applications and tag libraries. JSP 2.0 makes the EL available to all JSP applications and all tag libraries (including the old taglibs designed for JSP 1.x). JSP 2.0 also simplifies tag library development, as you'll see later in this article.

Since its first version, JSP has provided standard tags for using JavaBeans in JSP pages. You can create or find JavaBean instances with <jsp:useBean>, and then you can get and set their properties with <jsp:getProperty> and <jsp:setProperty>. With JSP 2.0, you may also get the value of a property with:

${bean.property}

In addition to properties, JavaBean components have public methods that often must be called from JSP pages. The remainder of this article will present three ways for calling JavaBean methods without using Java code. One is based on the JSP 2.0 support for functions, which are EL constructs that allow you to call Java classes' static methods. Another solution uses custom tags that get the method parameters as tag attributes. The third way is based on a generic tag that lets you call any public method of any JavaBean class from a JSP page.

Use functions

The initial JSTL 1.0 EL lacked support for functions. The JSP 2.0 EL lets you call a Java class's public static method using the following syntax:

${prefix:methodName(param1, param2, ...)}

The JSP function must be declared in a tag library descriptor (TLD):

<function>
    <name>methodName</name>
    <function-class>className</function-class>
    <function-signature>
        returnType methodName(param1Type, param2Type, ...)
    </function-signature>
</function>

The Java class doesn't have to implement any special interface. The only requirement is to make the Java method public and static.

The TestBean class

The TestBean class has a public method named testMethod(), which is called from the JSP pages presented in the following sections. The JavaBean has three properties named text, number, and logic. These properties are modified by testMethod(), which returns a string containing the three properties' modified values:

package com.devsphere.articles.calltag;
public class TestBean {
    private String text;
    private int number;
    private boolean logic;
    public TestBean() {
        text = "";
        number = 0;
        logic = false;
    }
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }
    public boolean getLogic() {
        return logic;
    }
    public void setLogic(boolean logic) {
        this.logic = logic;
    }
    public String testMethod(String text, int number, boolean logic) {
        setText(getText() + text);
        setNumber(getNumber() + number);
        setLogic(getLogic() || logic);
        StringBuffer buf = new StringBuffer();
        buf.append(getText());
        buf.append(' ');
        buf.append(getNumber());
        buf.append(' ');
        buf.append(getLogic());
        return buf.toString();
    }
}

The TestFunction class

Because the JSP 2.0 EL allows only calls to static methods, TestBean's testMethod() must be wrapped in a static method. The TestFunction class provides such a static wrapper that takes the same parameters as the bean method plus the bean object whose method must be called:

package com.devsphere.articles.calltag;
public class TestFunction {
    public static String testMethod(TestBean object,
            String text, int number, boolean logic) {
        return object.testMethod(text, number, logic);
    }
}

The compiled TestFunction.class file must be placed together with TestBean.class into the Web application's /WEB-INF/classes directory. As an alternative, the two classfiles can be packed in a jar file and stored in /WEB-INF/lib.

The TestFunction JSP

Before calling the testMethod() function, the TestFunction.jsp page must specify the function's prefix and the library's Uniform Resource Identifier (URI):

<%@ taglib prefix="tf" uri="http://devsphere.com/articles/calltag/TestFunction.tld"%>

The <jsp:useBean> tag creates an instance of the TestBean class:

<jsp:useBean id="obj" class="com.devsphere.articles.calltag.TestBean"/>

The testMethod() function is called twice. The first call gets some constant parameters, while the second call gets the values of the bean properties as parameters:

<HTML>
<BODY>
${tf:testMethod(obj, "abc", 123, true)}
<HR>
${tf:testMethod(obj, obj.text, obj.number, obj.logic)}
</BODY>
</HTML>

The TestFunction.jsp page produces the following HTML output:

<HTML>
<BODY>
abc 123 true
<HR>
abcabc 246 true
</BODY>
</HTML>

The TestFunction TLD

As mentioned earlier, the JSP function must be declared in a tag library descriptor. The TestFunction.tld file defines some version number, the tf short name used in JSP pages as prefix for testMethod(), the library's URI, the function's name, the name of the class containing the static method, and the method's signature. The URI doesn't have to point to an existing Web resource, but it must be unique. You may not use the same URI for two different tag libraries.

Here is the TestFunction.tld file's content:

<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd"
    version="2.0">
    <tlib-version>1.0</tlib-version>
    <short-name>tf</short-name>
    <uri>http://devsphere.com/articles/calltag/TestFunction.tld</uri>
    <function>
        <name>testMethod</name>
        <function-class>
            com.devsphere.articles.calltag.TestFunction
        </function-class>
        <function-signature>
            java.lang.String testMethod(
                com.devsphere.articles.calltag.TestBean,
                java.lang.String, int, boolean)
        </function-signature>
    </function>
</taglib>

The TestFunction.tld file must be placed into the Web application's /WEB-INF directory. The same directory also contains the web.xml application descriptor, which declares the library within a <taglib> element. The URI that identifies the library in JSP pages and the TLD file's location are specified within two separate XML elements, <taglib-uri> and <taglib-location>:

<taglib>
    <taglib-uri>
        http://devsphere.com/articles/calltag/TestFunction.tld
    </taglib-uri>
    <taglib-location>
        /WEB-INF/TestFunction.tld
    </taglib-location>
</taglib>

Use custom tags

Tag libraries were introduced by JSP 1.1, which defined the Tag and BodyTag interfaces. JSP 1.2 added IterationTag and support for catching exceptions. These interfaces have handler methods such as doStartTag(), doInitBody(), doAfterBody(), and doEndTag(). Once you understand how these methods should be implemented, it's easy to build tag libraries. However, many developers viewed JSP 1.x's tag-handling mechanism as unnecessarily complex.

JSP 2.0 introduced a much simpler tag-handling protocol. If you extend the SimpleTagSupport class, you just have to implement the doTag() method for handling a JSP tag.

The TestMethodTag class

The TestMethodTag.jsp page calls the testMethod() JavaBean method using the following syntax:

<tm:testMethod object="..." text="..." number="..." logic="..."/>

When the application server translates the JSP page into a servlet, the above tag is replaced with a Java code fragment that calls the methods of a TestMethodTag instance created for handling the tag.

The tag handler extends the JSP 2.0 API's SimpleTagSupport class and defines one field for each attribute. These fields will maintain the tag attributes' values:

package com.devsphere.articles.calltag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
public class TestMethodTag extends SimpleTagSupport {
    private TestBean object;
    private String text;
    private int number;
    private boolean logic;

For each tag attribute, there must be a set method, which gets the attribute value and stores it in a field so that the tag handler can use it later:

    public void setObject(TestBean object) {
        this.object = object;
    }
    public void setText(String text) {
        this.text = text;
    }
    public void setNumber(int number) {
        this.number = number;
    }
    public void setLogic(boolean logic) {
        this.logic = logic;
    }

After setting the tag handler's attributes, the Java fragment (resulting from the JSP tag) invokes the tag handler's doTag() method, which calls the bean method. The doTag() method prints the string value returned by testMethod(). Therefore, the JSP output contains the returned value:

    public void doTag() throws JspException, IOException {
        String ret = object.testMethod(text, number, logic);
        JspWriter out = getJspContext().getOut();
        out.println(ret);
    }
}

The TestMethodTag2 class

Suppose you want to use the value returned by the bean method in a JSP. For example, you might have to pass it as an attribute value to another tag. Or, you might want to control its output in the JSP page:

<tm:testMethod2 object="..." text="..." number="..." logic="...">
... ${ret} ...
</tm:testMethod2>

The TestMethodTag2 handler class resembles TestMethodTag. Both have the same fields and methods, but the doTag() method is implemented differently:

package com.devsphere.articles.calltag;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
public class TestMethodTag2 extends SimpleTagSupport {
    private TestBean object;
    private String text;
    private int number;
    private boolean logic;
    public void setObject(TestBean object) {
        this.object = object;
    }
    public void setText(String text) {
        this.text = text;
    }
    public void setNumber(int number) {
        this.number = number;
    }
    public void setLogic(boolean logic) {
        this.logic = logic;
    }

The TestMethodTag2's doTag() method calls testMethod() and uses setAttribute() to create a JSP variable (named ret) that holds the object returned by the bean method within the page scope.

The TestMethodTag.jsp page uses the <tm:testMethod2> tag to call the bean method. Within <tm:testMethod2>'s body, the JSP page uses the ${ret} construct to get the value returned by the bean method.

The tag handler uses getJspBody() to obtain a JspFragment object that represents the body of the <tm:testMethod2> action. The JspFragment's invoke() method executes the Java code generated from the <tm:testMethod2>'s JSP body. In this example, the resulting HTML output goes to the JSP's writer, but the output could have been redirected to another writer by passing to invoke() a java.io.Writer object instead of null:

    public void doTag() throws JspException, IOException {
        String ret = object.testMethod(text, number, logic);
        JspContext context = getJspContext();
        context.setAttribute("ret", ret);
        JspFragment body = getJspBody();
        body.invoke(null);
    }
}

If you want a better understanding of the way in which the tag handler methods are invoked, look at the Java servlet generated by your application server to execute the JSP page. Tomcat 5.0 places the generated Java servlets in TOMCAT_HOME/work/Catalina/localhost/calltag/org/apache/jsp.

The TestMethodTag JSP

The TestMethodTag.jsp page uses the <%@taglib%> directive to specify the prefix of the tags and the library's URI:

<%@ taglib prefix="tm" uri="http://devsphere.com/articles/calltag/TestMethodTag.tld"%>

The <jsp:useBean> tag creates an instance of the TestBean class stored as a JSP page attribute named obj:

<jsp:useBean id="obj" class="com.devsphere.articles.calltag.TestBean"/>

The bean method is called twice with the help of the two custom tags handled by TestMethodTag and TestMethodTag2. The first call gets some constant parameters, while the second call gets the values of the bean properties as parameters. Both calls output the value returned by testMethod():

<HTML>
<BODY>
<tm:testMethod object="${obj}" text="abc" number="123" logic="true"/>
<HR>
<tm:testMethod2 object="${obj}"
    text="${obj.text}" number="${obj.number}" logic="${obj.logic}">
-- ${ret} --
</tm:testMethod2>
</BODY>
</HTML>

The TestMethodTag.jsp page produces the following HTML output:

<HTML>
<BODY>
abc 123 true
<HR>
-- abcabc 246 true --
</BODY>
</HTML>

The TestMethodTag TLD

The TestMethodTag.tld file describes the <tm:testMethod> and <tm:testMethod2> tags. Both have the same list of attributes: object, text, number, and logic. All attributes are required (none are optional). Any attribute's value can be either a constant or an expression (because rtexprvalue is true). The first tag's body must always be empty, while the second tag's body may contain scriptless JSP code:

<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd"
    version="2.0">
    <tlib-version>1.0</tlib-version>
    <short-name>tm</short-name>
    <uri>http://devsphere.com/articles/calltag/TestMethodTag.tld</uri>
    <tag>
        <name>testMethod</name>
        <tag-class>com.devsphere.articles.calltag.TestMethodTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>object</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>text</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>number</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>logic</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
    <tag>
        <name>testMethod2</name>
        <tag-class>com.devsphere.articles.calltag.TestMethodTag2</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
            <name>object</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>text</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>number</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>logic</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>

The tag library is declared in the web.xml application descriptor:

<taglib>
    <taglib-uri>
        http://devsphere.com/articles/calltag/TestMethodTag.tld
    </taglib-uri>
    <taglib-location>
        /WEB-INF/TestMethodTag.tld
    </taglib-location>
</taglib>

Use a generic tag

So far, this article has presented two ways for calling JavaBean methods from scriptless JSP pages. Both ways require some work that may be worthwhile for frequently called methods. In the case of JSP functions, you must provide static wrappers for nonstatic methods, and you must declare the functions in a TLD file. If you prefer to use custom tags, you have to code the tag handler classes and describe the custom tags in a TLD file. A third way for calling JavaBean methods doesn't require extra work. You may use a generic tag based on the Java Reflection API and the new JSP 2.0 dynamic attributes.

The CallTag class

The generic tag has the following syntax:

<ct:call object="..." method="..." 
    return="..." scope="..." debug="..."
    param1="..." param2="..." ... />

The CallTag class extends SimpleTagSupport and overrides the doTag() method like the tag handlers from the earlier sections. In addition, the CallTag class implements the DynamicAttributes interface, whose setDynamicAttribute() method lets you handle attributes not described in the TLD file:

package com.devsphere.articles.calltag;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.DynamicAttributes;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class CallTag extends SimpleTagSupport implements DynamicAttributes {

The CallTag class declares a field for each tag attribute. The paramValues list will maintain the dynamic attributes' values, which are actually the values that will be passed as parameters to the JavaBean method:

    private Object object;
    private String methodName;
    private String returnVar;
    private int returnScope;
    private boolean debugFlag;
    private List paramValues;

Some of the fields are initialized with default values within the CallTag() constructor. The other fields don't need initialization because their corresponding attributes are required or because the default Java values such as null and false are suitable for them. Here is the CallTag() constructor:

    public CallTag() {
        returnScope = PageContext.PAGE_SCOPE;
        paramValues = new LinkedList();
    }

The object attribute specifies the bean instance whose method must be called. Usually, you create or find an object with <jsp:useBean id="someID" ... > and then call its methods using <ct:call object="${someID}" ... >:

    public void setObject(Object object) throws JspException {
        if (object == null)
            throw new JspException("Null 'object' attribute in 'call' tag");
        this.object = object;
    }

The method attribute specifies the JavaBean method name that must be called:

    public void setMethod(String methodName) throws JspException {
        if (methodName == null)
            throw new JspException("Null 'method' attribute in 'call' tag");
        if (methodName.length() == 0)
            throw new JspException("Empty 'method' attribute in 'call' tag");
        this.methodName = methodName;
    }

The return attribute is optional and specifies the JSP variable name that will hold the value returned by the JavaBean method:

    public void setReturn(String returnVar) throws JspException {
        if (returnVar == null)
            throw new JspException("Null 'return' attribute in 'call' tag");
        if (returnVar.length() == 0)
            throw new JspException("Empty 'return' attribute in 'call' tag");
        this.returnVar = returnVar;
    }

The scope attribute is optional too and specifies the return JSP variable's scope. The default scope is page. Other valid scopes are request, session, and application:

    public void setScope(String returnScope) throws JspException {
        if (returnScope.equalsIgnoreCase("page"))
            this.returnScope = PageContext.PAGE_SCOPE;
        else if (returnScope.equalsIgnoreCase("request"))
            this.returnScope = PageContext.REQUEST_SCOPE;
        else if (returnScope.equalsIgnoreCase("session"))
            this.returnScope = PageContext.SESSION_SCOPE;
        else if (returnScope.equalsIgnoreCase("application"))
            this.returnScope = PageContext.APPLICATION_SCOPE;
        else
            throw new JspException("Invalid 'scope' in 'call' tag: "
                + returnScope);
    }

If the debug attribute's value is true, the doTag() method includes an HTML comment in the JSP output that offers information about the method call: class name, method name, parameter values, and returned value. By default, debug is false:

    public void setDebug(boolean debugFlag) throws JspException {
        this.debugFlag = debugFlag;
    }

The above set methods are called from the servlet automatically generated from the JSP page by the application server. The JavaBean method's parameters are obtained as dynamic tag attributes that aren't described in CallTag.tld. For each of them, the generated servlet calls the setDynamicAttribute() method, which adds the attribute value to the paramValues list. The attribute name is ignored. Normally, you use the parameter name as the attribute name to make the code easy to understand. However, if the JavaBean method has a parameter called object, method, return, scope, or debug, you can use some other string as the attribute name:

    public void setDynamicAttribute(String uri, String localName, Object value)
        throws JspException {
        paramValues.add(value);
    }

The doTag() method is invoked in the generated servlet after all attributes are set. The call to the JavaBean method is delegated to the MethodCaller class described in the next section. If the return attribute is present and the JavaBean method returns a primitive value or a non-null object, doTag() creates a JSP variable holding the returned value. As mentioned earlier, if debug is true, some debugging information is outputted within an HTML comment. You can find out the JavaBean's class name, method name, parameter values, and the returned value, if you view the HTML code produced by a JSP that uses <ct:call ... debug="true" ... >:

    public void doTag() throws JspException, IOException {
        Object returnValue
            = MethodCaller.call(object, methodName, paramValues);
        JspContext context = getJspContext();
        if (returnVar != null) {
            if (returnValue != null)
                context.setAttribute(returnVar, returnValue, returnScope);
            else
                context.removeAttribute(returnVar, returnScope);
        }
        if (debugFlag) {
            JspWriter out = context.getOut();
            out.println("<!-- calltag debug info");
            out.println("Class: " + object.getClass().getName());
            out.print("Call: " + methodName + "(");
            Iterator paramIterator = paramValues.iterator();
            while (paramIterator.hasNext()) {
                Object value = paramIterator.next();
                out.print(value != null ? value.toString() : "null");
                if (paramIterator.hasNext())
                    out.print(", ");
            }
            out.println(")");
            if (returnVar != null)
                out.println("Return: "
                    + (returnValue != null ? returnValue.toString() : "null"));
            out.println("-->");
        }
    }
}

The MethodCaller class

The MethodCaller class uses the Java Reflection API to invoke JavaBean methods. It also uses a few classes taken from the Apache JSTL implementation to handle type conversions:

package com.devsphere.articles.calltag;
import com.devsphere.articles.calltag.jstl.Coercions;
import com.devsphere.articles.calltag.jstl.ELException;
import com.devsphere.articles.calltag.jstl.Logger;
import javax.servlet.jsp.JspException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
public class MethodCaller {

The findMethod() static method gets the array of Method instances describing the JavaBean class's public methods. Then, it iterates over this array and returns the first Method that has the given name and the given number of parameters:

    private static Method findMethod(
            Object object, String methodName, int paramCount)
            throws JspException {
        Class clazz = object.getClass();
        Method pubMethods[] = clazz.getMethods();
        for (int i = 0; i < pubMethods.length; i++) {
            Method m = pubMethods[i];
            if (methodName.equals(m.getName())
                    && m.getParameterTypes().length == paramCount)
                return m;
        }
        throw new JspException("Method not found: "
            + clazz.getName() + "." + methodName + "()");
    }

The coerceParamValuesToParamTypes() method coerces the <ct:call>'s dynamic attribute values to the JavaBean method's parameter types. The Coercions class's coerce() method developed by Apache does the hard work. This method may throw an ELException, whose message and root cause are wrapped within a JspException:

    private static Object[] coerceParamValuesToParamTypes(
            Method method, List paramValues)
            throws JspException {
        Class paramTypes[] = method.getParameterTypes();
        Object coercedValues[] = new Object[paramTypes.length];
        Iterator paramIterator = paramValues.iterator();
        Logger logger = new Logger(System.err);
        for (int i = 0; i < paramTypes.length; i++) {
            Object paramValue = paramIterator.next();
            Class paramClass = paramTypes[i];
            if (paramValue == null || paramValue.getClass() != paramClass)
                try {
                    paramValue = Coercions.coerce(
                        paramValue, paramClass, logger);
                } catch (ELException e) {
                    throw new JspException(e.getMessage(), e.getRootCause());
                }
            coercedValues[i] = paramValue;
        }
        return coercedValues;
    }

The call() method receives as parameters the JavaBean object, the name of the method that must be invoked, and a list containing the parameter values. It invokes findMethod() to get the Method instance. It invokes coerceParamValuesToParamTypes() to apply type conversions. And finally, it invokes the JavaBean method. The CallTag class uses the public call() method:

    public static Object call(
            Object object, String methodName, List paramValues)
            throws JspException {
        Method method = findMethod(object, methodName, paramValues.size());
        Object args[] = coerceParamValuesToParamTypes(method, paramValues);
        try {
            return method.invoke(object, args);
        } catch (InvocationTargetException e) {
            throw new JspException(e.getTargetException());
        } catch (IllegalAccessException e) {
            throw new JspException(e);
        } catch (IllegalArgumentException e) {
            throw new JspException(e);
        }
    }
}

The TestCallTag JSP

The TestCallTag.jsp page uses the <%@taglib%> directive to declare that it uses the tag library described by CallTag.tld:

<%@ taglib prefix="ct" uri="http://devsphere.com/articles/calltag/CallTag.tld"%>

Like the previous JSP pages, TestCallTag.jsp creates a bean instance with <jsp:useBean>:

<jsp:useBean id="obj" class="com.devsphere.articles.calltag.TestBean"/>

The TestBean class's method is called twice using the generic <ct:call> tag. The values returned by testMethod() are obtained through two JSP variables named ret and ret2:

<HTML>
<BODY>
<ct:call object="${obj}" method="testMethod" debug="true"
    text="abc" number="123" logic="true"
    return="ret"/>
${ret}
<HR>
<ct:call object="${obj}" method="testMethod" debug="true"
    text="${obj.text}" number="${obj.number}" logic="${obj.logic}"
    return="ret2"/>
${ret2}
</BODY>
</HTML>

The TestCallTag.jsp page produces the following HTML output. Some debugging information is generated within HTML comments because the debug flag is true:

<HTML>
<BODY>
<!-- calltag debug info
Class: com.devsphere.articles.calltag.TestBean
Call: testMethod(abc, 123, true)
Return: abc 123 true
-->
abc 123 true
<HR>
<!-- calltag debug info
Class: com.devsphere.articles.calltag.TestBean
Call: testMethod(abc, 123, true)
Return: abcabc 246 true
-->
abcabc 246 true
</BODY>
</HTML>

The CallTag TLD

The CallTag.tld describes the <ct:call> tag. The object and method attributes are required. The return, scope, and debug attributes are optional. The <dynamic-attributes> element must contain true so that the method parameters (text, number, and logic) can be accepted as dynamic attributes at runtime instead of declaring them like in TestMethodTag.tld:

<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd"
    version="2.0">
    <tlib-version>1.0</tlib-version>
    <short-name>ct</short-name>
    <uri>http://devsphere.com/articles/calltag/CallTag.tld</uri>
    <tag>
        <name>call</name>
        <tag-class>com.devsphere.articles.calltag.CallTag</tag-class>
        <body-content>empty</body-content>
        <dynamic-attributes>true</dynamic-attributes>
        <attribute>
            <name>object</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>method</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>return</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
        <attribute>
            <name>scope</name>
            <required>false</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
        <attribute>
            <name>debug</name>
            <required>false</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>

The tag library is declared in the web.xml application descriptor:

<taglib>
    <taglib-uri>
        http://devsphere.com/articles/calltag/CallTag.tld
    </taglib-uri>
    <taglib-location>
        /WEB-INF/CallTag.tld
    </taglib-location>
</taglib>

You may use the <ct:call> tag to invoke methods of your own JavaBeans from JSP pages. You just have to include the CallTag library in your Web application. No customization is needed.

Go scriptless

In this article, you've learned how to call JavaBean methods from scriptless JSP pages. The JSP code based on functions is more compact and similar to the equivalent Java code, but you have to declare all functions in TLD files and provide wrappers for nonstatic methods. The use of custom tags makes the code more readable because the names of the method parameters appear as attribute names. The generic call tag requires a little more typing for the code that makes the call, but you don't have to declare any function, provide wrappers, or develop a tag handler for each JavaBean method.

Andrei Cioroianu is the founder of Devsphere, a provider of custom Java Web development services and tools, specializing in Java XML solutions based on free open source software. Andrei is the author of the XML JSP Tag Library (XJTL), which complements the JSP Standard Tag Library (JSTL) with XML-related features such as SAX-based parsing, mixed SAX-DOM parsing, DOM serialization, and dynamic XML output. Currently, Andrei is migrating XJTL (and several Web applications based on it) from JSP 1.2 to JSP 2.0. Andrei is also coauthor of Java XML Programmer's Reference (2001; ISBN: 1861005202) and Professional Java XML (2001; ISBN: 186100401X) published by Wrox Press.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies