Techminded

JSP Custom Functions

We oftern have to add some comprehensive logic to our views so templates may got bloated code or markup structure that is not good for project maintainability and karma. The siplisiest ways to avoid this is to move some reusable parts to functions that can be defined in JSP files with <!% %> tag just like this:

<%!
    Map<Long,String> globalIdList = new HashMap<Long, String>();
    public String someHelper(Map<Long,String> globalIdList, List elementId, String elementTitle, RenderRequest request) {
        if (globalIdList.containsKey(elementId)) {
            return "";
        } else {
            globalIdList.put(elementId, elementTitle);
        }
        String output = "<div>";
        output += elementTitle;
        output += "</div>";
        return output;
    }
%>
...

<% for (Map.Entry<Long,String> entry: elementList.entrySet())) { %>
    <%= someHelper(globalIdList, entry.getKey(), entry.getValue(), renderRequest); %>
<% } 
globalIdList = new HashMap<Long, String>();
%>

the exaple above allows you to move some sophisticated logic to separate function that's declaration can be included with other jsp(x) file and not break developer's eyes and sould every time he opens main rendring template. Also semi global variable globalIdList is presented to demostrate how you can introduce semi global variable behaviour. This can look even better with jstl syntax but still not looks like totally familar to mvc era technologies. The other approach is to write own tag functions than you can find easier than you may expect. We will introduce newline-to-br converting function to have proper displaying for texts from textareas in html views. First of all we will write our business logic class. I assume you use some simple web project, this example is extracted from working liferay's project. So we creeate com.somepackage.TagNl2br.java file with the following contents:

package com.somepackage;


import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;

public class TagNl2br extends BodyTagSupport {

    @Override
    public int doAfterBody() throws JspException {
        try {
            BodyContent bodycontent = getBodyContent();
            String body = bodycontent.getString();
           JspWriter out = bodycontent.getEnclosingWriter();
           if (body != null) {
               out.print(nl2br(body));
           }
       } catch (IOException ioe) {
           throw new JspException("Error:" + ioe.getMessage());
       }
       return SKIP_BODY;
    }

    private static String nl2br(String s) {
        return s.replaceAll("\n", "<br>\n");
    }
}

Than we should add declaration for our tag, we create new tag library (.tld) file with path /WEB-INF/tld/functions.tld with the following contents:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
    <tlib-version>1.0</tlib-version>
    <jsp-version>1.2</jsp-version>
    <short-name>functions</short-name>
    <tag>
        <name>nl2br</name>
        <tag-class>com.somepackage.TagNl2br</tag-class>
        <body-content>JSP</body-content>
    </tag>
</taglib>

It will allow us to use <f:nl2br>Some Text\n\nNew line</f:nl2br> tags or ${f:nl2br("Some Text\n\nNew Line")} if you use JSTL in JSP files, but only after we declare tag library in web.xml add the following just inside web-app tag body:

<jsp-config>
    <taglib>
        <taglib-uri>http://example.com/functions</taglib-uri>
        <taglib-location>/WEB-INF/tld/functions.tld</taglib-location>
    </taglib>
</jsp-config>

and import in our JSP file or it's init header:

<%@ taglib uri="http://example.com/functions" prefix="f" %>

And that's it:) !

Comments