Tagfiles VS Taglibs

I recently had the realization that I was duplicating a certain set of code across multiple portlets. For those who know me, they will attest that my work motto is to work smart not hard. I hate code duplication with a fiery passion, thus something had to be done. The question was what? After some thought, I decided that I needed to make a taglib. I started coding it, and had a realization... a tagfile would be much easier!

So what is a tagfile? Tagfiles were introduced with JSP 2.0. They were created because sometimes a taglib is jsut too much work for what it does. For many simple and even some complex operations, tagfiles are like coding magic.  Let's compare the two:

  • Taglibs:
  • Class driven
  • Uses a tld that can be from any site
  • JSP 1.1 compatible.
  • Tagfiles
    • JSP driven
    • No tld required
    • Works immediately without any compile
    • Really easy!

So if you can't guess, I chose to use a tagfile. Before I show you the code, let's do a high level overview of what is required for tagfiles.

  1. You
  2. A tags folder under WEB-INF (/WEB-INF/tags)
  3. A folder under that with the name of the tag
  4. a .tag file

So lets make a basic Hello World tagfile that outputs the text "Hello World in (Insert Year Here)".

 

Step 1: create /WEB-INF/tags/hello-world/hello.tag
Step 2: enter the following in hello.tag:

<%@attribute name="year" required="true"%>

Hello World in <%=year%>

Step 3: Edit your JSP that you want the hello world to show up in. Enter this code:

<%@ taglib prefix="showMore" tagdir="/WEB-INF/tags/hello-world" %>
<showMore:hello />

That should be all that's required. Refresh your jsp. Eat cake.

So sure, that's cool and all... but you could EASILY accomplish the same this with a simple file include. Where is the excitement? The drama? It's right here, you just don't know it yet.

Let's do a more useful example. Let's say that you hate using our <portlet:actionURL /> taglib... you want something easier and cleaner. I know in my IDE it always breaks my <a href's /> when I put it in. Time to fix that. This tagfile will do the following: create a Struts url (maximized or minimized) to a struts path, if a clean way that IDEs understand.

Step 1: create init.tag under hello-world
Step 2: enter the following in init.tag

<%@ tag import="com.liferay.portal.kernel.security.permission.ActionKeys" %>
<%@ tag import="com.liferay.portal.kernel.util.StringPool" %>
<%@ tag import="com.liferay.portal.service.permission.PortletPermissionUtil" %>

<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<%@ taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %>
<%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %>

<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
<%@ taglib uri="http://struts.apache.org/tags-nested" prefix="nested" %>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>

<%@ tag import="com.liferay.portal.util.PortalUtil" %>
<%@ tag import="com.liferay.portal.util.PortletKeys" %>
<%@ tag import="javax.portlet.WindowState" %>
<%@ tag import="java.text.DateFormat" %>
<%@ tag import="java.util.List" %>

<%@attribute name="maximized" type="java.lang.Boolean"%>
<%@attribute name="struts_path" required="true" %>
<%@attribute name="actionUrl" type="java.lang.Boolean"%>

<portlet:defineObjects />
<liferay-theme:defineObjects />

<%
    String currentURL = PortalUtil.getCurrentURL(request);
%>

Notice that this looks a lot like a regular liferay init.jsp... that's because it almost is! We just replace <%@page %> with <%@tag %>.

Step 3: edit hello.tag with the following code:

<%@include file="init.tag"%>

<%
    String windowState=null;
    if (maximized != null) {
        windowState = WindowState.MAXIMIZED.toString();
    }
    else {
        windowState = WindowState.NORMAL.toString();
    }
%>
<%
    if (actionUrl != null) {
%>
    <portlet:actionURL windowState="<%=windowState%>">
        <portlet:param name="struts_action" value="<%=struts_path%>" />
    </portlet:actionURL>
<%
    }
    else {
%>
    <portlet:renderURL windowState="<%=windowState%>">
        <portlet:param name="struts_action" value="<%=struts_path%>" />
    </portlet:renderURL>
<%
    }
%>

Step 4: Lastly, enter this into your jsp.

<showMore:hello struts_path="/a/view" maximized="true" actionUrl="true"/>

Take note that your struts path will change, and that struts_path is the only one required...

Step 5: Reload your page, enjoy your beautiful link.

Step 6: Enjoy more cake.

Blogs
That's pretty cool. Traditional taglibs can be quite painful for the simpler tasks (such as macros like these).

For the more complex tasks, such as dealing with nested content between the tags, the pain is more bearable.
great work. I had a similar problem than you: two portlets, one extending some of the other pages, but both sharing several jsps. Now I can mantin common jsps only in one portlet... and I learned tagfiles! thank you
nevermind my last post. I couldn't solve the problem of sharing jsps between portlets... can you point me on how you make it? thanks in advance and excuse my poor english. great article about filetags, anyway...
Hey Sabastian,

Using tagfiles will work to share a common jsp between 2 pages. You could also create one and do a simple <%@ include="filenamehere.jsp" %> to include a common file (this is done everywhere in Liferay). Your best bet would be to post a question in the public messageboards about this and shoot me a link so I can answetr it there. I want to try to keep threads in the blogs related to the subject at hand.

Thanks!!

Brett
Brett: thanks for the reply. I have posted my problem here: http://www.liferay.com/web/guest/community/forums/message_boards/message/718805

I was able to resolve it configuring struts-path for the second portlet to point to the original portlet tiles def... but any other idea is welcome. thank you for your help anyway.
what about "var" <portlet:actionURL> attribute? can it be simulated by your <showMore:hello> custom tag ?
I got it. in your init.tag add:

<%@attribute name="javaVar" rtexprvalue="false" required="true" %>
<%@ variable name-from-attribute="javaVar"
variable-class="java.lang.Object" alias="var" scope="AT_END"%>

in your hello.tag you must pass var attribute to <portlet:actionURL and <portlet:renderURL like: <portlet:actionURL windowState="<%=windowState%>" var="var">

then in the caller jsp you can get the url like:
<showMore:hello struts_path="/portlet_a/view" maximized="true" actionUrl="true" javaVar="var1"/>

<p>${var1}</p>

another sugestion is to set the portlet path inside the tag file so the user don't hardcode the portlet struts path in its jsp. Inside the tag file you can obtain the portlet struts path with

String portletStrutsPath = PortletLocalServiceFactory.getImpl().getPortletById(company.getCompanyId(), portletName).getStrutsPath()

So the user can write

<showMore:hello struts_path="view" ...

instead of

<showMore:hello struts_path="/portlet_a/view" ....

hope you can understand my bad english

cheers