Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Call JavaBean methods from JSP 2.0 pages

Develop custom tags with dynamic attributes

  • Print
  • Feedback

Page 6 of 6

    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.

About the author

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.

Read more about Core Java in JavaWorld's Core Java section.

  • Print
  • Feedback

Resources