JSP e tag files: criando templates em 10 minutos

Este mês têm sido bastante curioso pelo fato de três pessoas terem me feito a mesma pergunta: “- Você largou o Java de vez?”. A resposta é não, mas, para não deixar dúvidas, eu resolvi voltar a escrever sobre Java. O assunto foge um pouco do que costumo escrever, mas é algo que considero importante.

Ultimamente, tenho visto a forma como certos desenvolvedores escrevem seus JSPs, e o que me preocupou foi ver o quanto eles desconhecem a tecnologia. Não é raro achar páginas cheias de scriptlets, includes malucos, código du-tri-quadri-plicado e nenhuma taglib além da fornecida pelo framework web em uso.

É muito fácil escrever JSPs da forma errada, mas fazer as coisas de um jeito melhor é mais fácil ainda. Ao contrário do que muitos pensam, é possível sim criar JSPs limpos sem o uso de qualquer framework de layout (como Sitemesh) ou biblioteca (com exceção da JSTL, é claro).

Um exemplo

O primeiro passo é definir o template base para as páginas da aplicação. Considere o seguinte exemplo de XHTML e sua representação gráfica:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
        <meta name="keywords"    content="keyword1, keyword2, keyword3" />
        <meta name="description" content="Page description" />
        <title>Page title</title>
 
        <!-- Essential scripts -->
        <link rel="stylesheet" type="text/css" href="css/style.css"/>
        <script type="text/javascript" src="js/query.js"></script>
 
        <!-- Extra header -->
        <script type="text/javascript" src="js/util.js"></script>
    </head>
    <body>
        <!-- Main -->
        <div id="wrapper">
            <div id="header"></div>
            <div id="content"></div>
            <div id="footer"></div>
        </div> <!-- Main -->
 
        <script type="text/javascript">
            // Other scripts (e.g. Google Analytics)
        </script>
    </body>
</html>
Representação gráfica do layout

Representação gráfica do layout

O que costuma acontecer, mesmo em um layout simples como esse, é o alto índice de duplicação de código entre as diferentes páginas, principalmente em função do uso de Copy and Paste Programming. Muitos desenvolvedores por aí escreveriam o seguinte JSP para representar o layout proposto:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
        <meta name="keywords"    content="keyword1, keyword2, keyword3" />
        <meta name="description" content="Page description" />
        <title>Page title</title>
 
        <!-- Essential scripts -->
        <jsp:include page="scripts.jsp"/>
 
        <!-- Extra header -->
        <script type="text/javascript" src="js/util.js"></script>
    </head>
    <body>
        <!-- Main -->
        <div id="wrapper">
            <jsp:include file="header.jsp"/>
            <div id="content">Page content here</div>
            <jsp:include page="footer.jsp"/>
        </div> <!-- Main -->
 
        <script type="text/javascript">
            // Other scripts
        </script>
    </body>
</html>

Viu o problema? De nada adianta separar os pedaços com e continuar duplicando código. O ideal seria traduzir o layout em algo assim:

<%@ taglib tagdir="/WEB-INF/tags/layout" prefix="layout" %>
<layout:page title="Title" description="Page description" keywords="keyword1, keyword2, keyword3">
    <jsp:attribute name="extraHeader">
        <script type="text/javascript" src="js/util.js"></script>
    </jsp:attribute>
    <jsp:attribute name="extraBottom">
        // Other scripts (e.g. Google Analytics)
    </jsp:attribute>
    <jsp:body>
        Page content here
    </jsp:body>
</layout:page>

A boa notícia é que isso é possível, através de tag files.

Reúso de conteúdo com tag files

Existem pelo menos três formas de se criar custom tags em JSP, cada qual com seu propósito: classic tag handlers, simple tag handlers e tag files. Neste post tratarei somente das tag files, mas saber como e quando utilizar simple tag handlers também é extremamente recomendado a todos que usam JSP no dia-a-dia.

A primeira coisa a fazer é criar o diretório WEB-INF/tags/layout. É nesse diretório que as tag files referentes ao template devem ficar. Agora, crie dois arquivos neste diretório, um para o cabeçalho e outro para o rodapé:

header.tag

<%@ tag body-content="empty" description="Header tag file" %>
<div id="header">
    <!-- Header section here -->
</div>

footer.tag

<%@ tag body-content="empty" description="Footer tag file" %>
<div id="footer">
    <!-- Footer section here -->
</div>

Como podemos ver, as tag files são como JSPs, mas com algumas propriedades especiais.

A última tag file, page.tag, define o esqueleto das páginas da aplicação:

<%@ tag description="Page layout" %>
 
<%@ attribute name="title"       required="true" description="Page title" %>
<%@ attribute name="keywords"    required="true" description="Page keywords to improve SEO" %>
<%@ attribute name="description" required="true" description="Page description" %>
 
<%@ attribute name="extraHeader" fragment="true" description="Extra code to put before </head>" %>
<%@ attribute name="extraBottom" fragment="true" description="Extra code to put before </body>" %>
 
<%@ taglib tagdir="/WEB-INF/tags/layout" prefix="layout" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
 
        <!-- Displaying the attributes using EL -->
        <meta name="keywords"    content="${keywords}" />
        <meta name="description" content="${description}" />
        <title>${title}</title>
 
        <!-- Essential scripts -->
        <link rel="stylesheet" type="text/css" href="css/style.css"/>
        <script type="text/javascript" src="js/query.js"></script>
 
        <!-- Process the given input fragment -->
        <jsp:invoke fragment="extraHeader"/>
    </head>
    <body>
        <!-- Main -->
        <div id="wrapper">
            <!-- Renders the page header -->
            <layout:header/>
 
            <!-- Renders the tag body inside a DIV -->
            <div id="content"><jsp:doBody/></div>
 
            <!-- Renders the page footer -->
            <layout:footer/>
        </div> <!-- Main -->
 
        <!-- Process the given input fragment -->
        <jsp:invoke fragment="extraBottom"/>
    </body>
</html>

Esta última tag file é mais complexa que as anteriores, pois recebe dados e blocos de código (através de fragments). Além disso, ela aceita um corpo, que é executado com <jsp:doBody/>.

O exemplo usado aqui é razoavelmente simples e, por isso, não precisamos apelar para outros recursos mais avançados como variables e dynamic attributes. De qualquer forma, procure conhecer bem a tecnologia, pois você nunca sabe quando um ou outro recurso lhe pode ser útil.

Exemplo de uso

Novamente, um exemplo de uso das tags que criamos:

<%@ taglib tagdir="/WEB-INF/tags/layout" prefix="layout" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<layout:page title="Index page" description="This is the index page of my amazing app" keywords="amazing, app">
    <jsp:attribute name="extraHeader">
        <script type="text/javascript" src="js/util.js"></script>
        <script type="text/javascript" src="js/some_jquery_plugin.js"></script>
    </jsp:attribute>
    <jsp:attribute name="extraBottom">
        $(function() {
            // Call some JQuery function here, specific for this page
        });
    </jsp:attribute>
    <jsp:body>
        Welcome to my amazing app!<br/>
        1+1 = ${1+1}
 
        <ul>
        <c:forEach items="${objects}" var="obj">
            <li>${obj.attr}</li>
        </c:forEach>
        </ul>
    </jsp:body>
</layout:page>

Embora a aplicação como um todo siga um mesmo padrão visual, cada página pode necessitar de recursos adicionais (JavaScript, CSS, etc). Isso não chega a ser um problema, pois nosso template contempla a inclusão de conteúdo extra, se necessário.

Outra vantagem é que as tag files permitem o uso de custom tags e unified EL, mas proíbem o uso de scriptlets, o que já é meio caminho andado rumo a um JSP mais limpo.

Leitura recomendada

Leave a Reply