Jsp теги, новые и улучшенные

Добавлено : 22 Mar 2009, 09:51

Одной из важнейших и, к сожалению, плохо оцененных возможностей jsp являются пользовательские теги. В прошлой своей статье рассказывающей об jsp я упоминал о сложившейся на первых этапах развития jsp ситуации когда все-кому-не-лень создавали собственные теги, зачастую изобретая никому не нужные велосипеды в виде очередного тега IF, FOR и т.д. Таким образом, появление jstl стало важным этапом унификации и дало единую точку применения силы многих тысяч java-разработчиков.

Тем не менее, необходимость создания собственных тегов никуда не пропала, а идеи создания сайтов-приложений из множества кусочков, где элементарная единица это не параграф, заголовок или тег span, а нечто более крупное и наделенное долей интеллекта (да я говорю про вычисления, возможность настроить поведение блока с помощью некоторых атрибутов тега). Так вот эта необходимость есть. Вот только создание тегов требует значительных усилий именно java-разработчиков, так что мы не можем делегировать часть ответственности верстальщикам.Ведь им нужны знания tag-библиотек, нужно знать, что и когда возвращать из методов, какие классы нужно наследовать. Кроме того, не решена проблема с переносом внутрь тега html-блоков кода и сложной процедурой тестирования тегов. Ведь для того чтобы перечитать заново определения тегов и обновленный java-код требуется перезапускать контекст веб-приложения и это медленно, дорого, да и просто раздражает. Естественным решением от sun было создать новые теги (новые правила их написания), улучшенные и не повторяющие ошибок предшественников. Фактически для создания таких тегов вам не нужно досконально знать java-tag-api, хватит знаний html, jstl и немного смекалки. Кроме того, процедура установки и тестирования таких тегов значительно облегчилась. Если вам интересно, то давайте перейдем ближе к делу.

В папке WEB-INF мы создадим подпапку с именем tags (это имя зарезервировано и менять его не стоит). Именно в эту папку мы будем складывать файлы с описаниями тегов (я уже говорил, что писать код на java мы не будем?). Для лучшего упорядочения можно создать подкаталоги внутри tags соответствующие разным тегам и служащие их логической группировки. Теперь в папке tags создается новый файл с именем HelloTag.tag (расширение tag и это важно). Не знаю в чем вы пишите код, но я пользуюсь intellij idea и крайне доволен тем, что для tag-файлов работает подсветка и мастер подсказок. Теперь надо в этом файле написать директивы, подсказывающие tomcat-у о том как будет себя вести тег, какие у него будут атрибуты, обязательные они или нет. Начну я с самого простого – создам тег, который выводит на экран слово “Hello”. Итак, я создал файл HelloTag.tag и поместил его внутрь каталога /WEB-INF/tags. Содержимое файла очень простое (да, да ничего больше кроме этой строки):

Hello from Tag

Теперь я возвращаюсь назад в jsp-файл и подключаю в нем новый набор тегов (пока состоящий из одного экземпляра). Используется та же директива что и для подключения “старших братьев” (tag-ов написанных на java) – директива taglib, также указывается атрибут “prefix”, а вот вместо атрибута uri с идентификатором библиотеки, нужно использовать атрибут tagdir и значением которого будет путь к каталогу с тегами:

<%@ taglib tagdir="/WEB-INF/tags" prefix="t" %>
<%@ taglib tagdir="/WEB-INF/tags/food" prefix="food" %>

Здесь я использовал две декларации: одна для файлов-тегов непосредственно расположенных внутри каталога tags, а второй директивой подключил файлы-теги расположенные в подкаталоге food. Теперь самое время вызвать тега в теле jsp-документа:

<t:HelloTag />

Перезапускать контекст веб-приложения не нужно, вы должны сразу же открыв страницу в браузере увидеть фразу “ Hello from Tag ”.

Ура, пример должен заработать.

Пользы от такого тега не много, ну разве что как очередной способ собрать страницу из множества статических кусочков (шапка, футер сайта …). Попробуем передать в тег данные для обработки. Эти данные бывают двух видов: тело тега и атрибуты. В любом случае мы должны будем в самом начале tag-файла указать специальные директивы (что мы принимаем и как этим будем пользоваться). Надо сказать, что это крайне удобно по сравнению со старыми java¬-тегами, когда приходилось поддерживать два раздельных набора файлов – код java и описание тегов виде tld-документа.

<%@ tag import="java.util.Date" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
 
<%= new Date() %>
Hello from Tag

Посмотрев на пример выше можно сказать только одно ну прямо как в обычном jsp. И чем же интересно отличается такой tag-файл от обычного jsp-файла, подключенного с помощью директивы “jsp:include”. На самом деле ничем, кроме возможности формализовать правила использования тега, а что касательно прямых вставок java в файл тега, то их нужно всегда избегать и пользоваться тем же jstl – я уверен, что такой вариант будет наиболее понятным для верстальщиков. Следующая вариация тега HelloTag будет принимать два атрибута-параметра: сообщение которое нужно вывести на экран и количество раз.

<t:HelloTag message="Привет пользователь" />

Теперь пример кода файла с тегом:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ attribute name="message"  required="true" rtexprvalue="true" type="java.lang.String" %>
<%@ attribute name="count"  required="false"  rtexprvalue="true" type="java.lang.Integer" %>
 
<c:set var="count" value="${empty count?20:count}" />
 
<h1>Messages</h1>
 
<c:forEach var="i" begin="1" end="${count}">
    <p>
        <c:out value="${i}" /> <c:out value="${message}" /> 
    </p>
</c:forEach>

Как видите в самом начале файла я с помощью директивы “attribute” указал то, какие переменные должны быть переданы тегу: обязательное число повторений и необязательный параметр - строка текста. В самом начале я проверяю значение параметра count и если оно не указано, то присваиваю ему 20. Далее привычный цикл с помощью jstl. Запустив ваше веб-приложение и удостоверившись что цикл отработал как ему надо. Откройте папку E:\Program_Files_2\apache-tomcat-6.0.14\work\Catalina\localhost\МОЙ-КОНТЕКСТ\org\apache\jsp\tag (ну или где там у вас установлен tomcat). В этой папке вы увидите, что tag-файл был скомпилирован в java-файл, а, открыв его, увидите код класса производного от SimpleTagSupport. Одним словом, все эти новомодные tag-и похожи на то как jsp-файлы преобразуются в сервлеты. Отсюда несколько выводов, несмотрня на похожеть записи выражений внутри tag-файла и jsp-файла, есть отличия. Так если я попытаюсь обратиться к объекту out или pageContext, то мне сообщат, что таких объектов нет, их действительно нет ведь мы не внутри сервлета, а внутри класса тега. А вот как я могу добиться сходной функциональности:

public void foo (){
   
try {
        ((
PageContext)getJspContext()).getOut().println (“bla-bla-bla”);
   
} catch (IOException e) {}
}

Как видите достаточно мне получить ссылку на объект getJspContext (который фактически является ссылкой на PageContext) и я могу внутри тела тега работать со столь привычными requet, response, out … Насколько оправдан такой подход сказать трудно и я стараюсь его избегать (полагая что тег не должен содержать сложных вставок на java), но мало ли какая бывает ситуация.

Атрибуты, получаемые моим тегом, бывают трех видов: обычные, динамические и вычисляемые пользователем. В первом случае я явно указываю имена этих атрибутов, затем при вызове тега веб-контейнер (tomcat) следит за тем, чтобы переменные были переданы тегу, а перед передачей выполняет вычисление значения атрибута, так что на вход тегу поступают уже готовые для обработки данные. Пример обычных атрибутов был показан выше. Теперь про динамические теги: это такие теги имена которых не фиксированы, фактически когда я вызываю тег то могу указать ему произвольный набор тегов, например:

<x:makeFoo foo=”apple” bar=”orange” tar=”grapes” />

Для того чтобы внутри тега иметь возможность обратиться к таким динамическим атрибутам необходимо в начале tag-файла записать директиву:

<%@ tag dynamic-attributes="varContainer" %>

Все динамические теги (естественно, в их перечень не должны попасть те теги, которые вы явно создали указав в директиве “attribute”) будут помещены в карту varContainer (тип переменной HashMap) и теперь я могу вывести на экран их значения.

<%@ tag dynamic-attributes="varContainer" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
 
<h1>dynamic variables</h1>
 
<c:forEach var="i" items="${varContainer}">
    <p>
        key = <c:out value="${i.key}" />;
        value = <c:out value="${i.value}" />
    </p>
</c:forEach>

Третий вид атрибутов – это вычисляемые пользователем атрибуты (в англоязычной литературе их называют fragment-атрибуты, что дает подсказку об их использовании) . обычные атрибуты не важно динамические или нет вычисляются до вызова тега, и вычисления делает веб-контейнер. С fragment-атрибутами все не так, контейнер ничего сам не вычисляет а передает внутрь тега код выражения которое сам тег может вычислить когда ему это потребуется. Вспоминая старые добрые времена и c++, я нахожу прямую аналогию с функционалами, когда в качестве параметра функции передавались ссылки на другие функции. Например, универсальная функция сортировки нуждается в том, чтобы сравнивать элементы между собой и как это делать … ведь функция универсальная. Единственным выходом было передать в функцию сортировки адрес еще одной функции выполняющей само сравнение. Следующий пример будет, нет, не про сравнение чисел, а про форматирование данных. Например, есть тег, выводящий список чисел от X до Y (это будут обычные атрибуты). Но каждое число должно быть отформатировано определенным образом, например, четные числа должны быть выделены красным цветом, а нечетные цифры – зеленым. Однако само форматирование не является частью тега, а должно быть задано как атрибут – fragment-атрибут.

Итак, пробуем, вот код тега. Обратите внимание на то что я в цикле проверяю значение переменной ${i} и если оно четное то делаю вызов (jsp:invoke) fragment-переменной с именем even_fragment, иначе вызываю вычисление атрибута odd_fragment.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ attribute name="start"  required="true" rtexprvalue="true" type="java.lang.Integer" %>
<%@ attribute name="end"  required="true"  rtexprvalue="true" type="java.lang.Integer" %>
<%@ attribute name="even_fragment"  required="true"  fragment="true" %>
<%@ attribute name="odd_fragment"  required="true"  fragment="true" %>
 
<h1>Variables with fragment-attributes</h1>
 
<c:forEach var="i" begin="${start}" end="${end}">
    <c:choose>
        <c:when test="${ i mod 2 eq 0}">
            <jsp:invoke fragment="odd_fragment" />
        </c:when>
        <c:otherwise>
            <jsp:invoke fragment="even_fragment" />
        </c:otherwise>
    </c:choose>
</c:forEach>

Теперь внимание, как мне в странице, которая вызывает данный тег передать ему не обычный атрибут, а целый сценарий вычислений. Делаем так:

<t:HelloTag start="1" end="10">
     <jsp:attribute name="even_fragment">
         <span style="color: red;">${i}</span>
     </jsp:attribute>
     <jsp:attribute name="odd_fragment">
         <span style="color: green;">${i}</span>
     </jsp:attribute>
 </t:HelloTag>

Передавать внутрь тега fragment-атрибуты можно только с помощью вложенного элемента jsp:attribute. Давайте теперь запустим пример и увидим … увидим что получилось то у нас получилось, вот только не до конца: блоки span с соответствующие оформлением шрифта в результирующий документ были вставлены, но без содержимого. Проще говоря, переменная i, которую я использую внутри fragment-атрибута, никак не ассоциируется с одной именной переменной, но уже внутри тега (счетчик цикла). Для пробы я объявил еще одну переменную на странице jsp и внутри тела fragment-атрибута попробую на нее сослаться.

<c:set value="20px" var="size" />
 
 <t:HelloTag start="1" end="10">
     <jsp:attribute name="even_fragment">
         <span style="color: red; font-size: ${size};">${i}</span>
     </jsp:attribute>
     <jsp:attribute name="odd_fragment">
         <span style="color: green; font-size: ${size};">${i}</span>
     </jsp:attribute>
 </t:HelloTag>

Снова смотрим, что получилось. В исходном коде страницы присутствуют теги span с нужной расцветкой и заданным размером шрифта 20px. Значит, когда я делаю вызов jsp:invoke, то переменные вычисляются относительно самой jsp-страницы, а не файла тега. Если задуматься, то можно понять логику, которой руководствовались sun-овцы. Однако что нам делать? Попробуем внутри тега организовать цикл по переменной i, но ее scope будет не областью по-умолчанию – область самой страницы (pageScope), а requestScope.

<c:forEach var="i" begin="${start}" end="${end}" >
    <c:set value="${i}" var="i" scope="request" />
    <c:choose>
        <c:when test="${ i mod 2 eq 0}">
            <jsp:invoke fragment="odd_fragment" />
        </c:when>
        <c:otherwise>
            <jsp:invoke fragment="even_fragment" />
        </c:otherwise>
    </c:choose>
</c:forEach>

В таком виде код работает. Вот только остается впечатление некачественного подхода, когда мы помещаем переменные в глобальные области, также раздражает подход с тем, что пользователь тега должен знать какое имя переменной используется внутри тега (а вдруг переменная i уже занята). Чтобы решить эту проблему давайте разберемся еще с одним механизмом в jsp-тегах – переменными. Атрибуты были хорошим способом передать внутрь тега какую-то информацию, переменные же наоборот служат для того чтобы из тега эту информацию извлечь и передать в вызывающую тег страницу. Основное внимание на декларацию переменной “<%@ variable name-given="i" %>”. Также обратите внимание на то, что я убрал из кода цикла присвоение переменной i значения ее же но в более широком Scope:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ attribute name="start"  required="true" rtexprvalue="true" type="java.lang.Integer" %>
<%@ attribute name="end"  required="true"  rtexprvalue="true" type="java.lang.Integer" %>
<%@ attribute name="even_fragment"  required="true"  fragment="true" %>
<%@ attribute name="odd_fragment"  required="true"  fragment="true" %>
<%@ variable name-given="i" %>
 
<h1>Variables with fragment-attributes</h1>
 
<c:forEach var="i" begin="${start}" end="${end}" >
    <c:choose>
        <c:when test="${ i mod 2 eq 0}">
            <jsp:invoke fragment="odd_fragment" />
        </c:when>
        <c:otherwise>
            <jsp:invoke fragment="even_fragment" />
        </c:otherwise>
    </c:choose>
</c:forEach>

Код вызывающий данный тег нисколько не изменился. Пробуем … все должно работать. Как видите переменные это способ передать из тега информацию “во внешний мир”. У директивы “variable” есть еще несколько интересных атрибутов. Например, “scope”, ее возможными значениями являются “AT_BEGIN”, “AT_END”, “NESTED”. Эти параметры управляют видимостью переменной за пределами вызванного тега. Значение по-умолчанию – NESTED – говорит, что переменная i будет иметь значение только внутри тега HelloTag – собственно говоря, именно это мне и нужно было: переменная внутри тега не мешает вызывающему коду. Однако бывают ситуации, когда рассчитанное внутри тега значение необходимо в вызывающей странице за пределами тега. В этом случае используйте такие значения атрибута scope, как “AT_BEGIN”, “AT_END”. Первый режим говорит, что переменная будет доступна от начального тега (от открывающего тега) до конца всей страницы. Второй режим – AT_END – делает переменную доступной сразу после завершения вызова тега (внутри тега переменные не доступны). Делать примеры я не буду, т.к. эти три режима очевидны. Давайте лучше решим еще одну проблему нашего тега с fragment-атрибутами – зарезервированное имя переменной i. Я хочу сделать так, чтобы имя переменной в которую будет помещено значение цифры (счетчика цикла) было бы не жестко фиксированной величиной I, а задавалось пользователем вызывающим тег. Обратите внимание на декларацию выше переменной и атрибут name-given="i". Таким образом я фиксировал имя перменной. Вместо name-given нужно использовать другой атрибут – name-from-attribute. Он говорит, что имя переменной будет указано как атрибут. Важно объявить также сам атрибут, и есть еще ограничения на его тип данных. Так тип атрибута cycleVar – должен быть строкой, атрибут должен быть обязательным, также должно быть запрещено вычислять значение атрибута с помощью expressions или EL. Последний шаг – указать для переменной значение атрибута alias. Именно указанное здесь имя переменной должно быть использовано везде в теле тега.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ attribute name="start"  required="true" rtexprvalue="true" type="java.lang.Integer" %>
<%@ attribute name="end"  required="true"  rtexprvalue="true" type="java.lang.Integer" %>
 
<%@ attribute name="even_fragment"  required="true"  fragment="true" %>
<%@ attribute name="odd_fragment"  required="true"  fragment="true" %>
 
<%@ attribute name="cycleVar"  required="true" rtexprvalue="false" type="java.lang.String" %>
<%@ variable name-from-attribute="cycleVar" alias="cycleVarAlias"  scope="NESTED" %>
 
<h1>Variables with fragment-attributes</h1>
 
<c:forEach var="cycleVarAlias" begin="${start}" end="${end}" >
    <c:choose>
        <c:when test="${ cycleVarAlias mod 2 eq 0}">
            <jsp:invoke fragment="odd_fragment" />
        </c:when>
        <c:otherwise>
            <jsp:invoke fragment="even_fragment" />
        </c:otherwise>
    </c:choose>
</c:forEach>

И пример использования тега:

<t:HelloTag start="1" end="10" cycleVar="j">
     <jsp:attribute name="even_fragment">
         <span style="color: red; font-size: ${size};">${j}</span>
     </jsp:attribute>
     <jsp:attribute name="odd_fragment">
         <span style="color: green; font-size: ${size};">${j}</span>
     </jsp:attribute>
</t:HelloTag>

На этом про атрибуты и переменные тегов все, давайте поработаем с телом тега. Прежде всего, мы должны вначале tag-файла указать директивной есть ли у нашего тега тело и какое оно:

<%@ tag body-content="empty" %>

Так я сказал что у тега не должно быть тела. Есть еще два значения: tagdependent и scriptless. Что они означают мы разберем на примерах, однако давайте сначала посмотрим как задать для тега тело. В случае если у тега обычные или динамические атрибуты (не fragment), то тело задается как всегда:

<t:HelloTag start="1" end="10">
     Привет, это тело тега
</t:HelloTag>

В случае если у вас есть fragment-теги, то тело тега задается с помощью еще одного специального элемента – body (с равным успехом такой стиль задания тела тега можно применять и без fragment-атрибутов – это дело вкуса):

<t:HelloTag start="1" end="10">
     <jsp:body>
         Привет, это тело тега
     </jsp:body>
</t:HelloTag>

Теперь передав внутрь тега тело, мы можем его вычислить. Дело в том, что если в самом tag-файле не будет записан явно вызов команды doBody, то тело цикла будет проигнорировано. Пробуем создать тег, который X раз выводит тело цикла на экран в рамках различного цвета в зависимости от того четное или нет сейчас значение счетчика цикла.

<%@ tag body-content="scriptless" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ attribute name="start"  required="true" rtexprvalue="true" type="java.lang.Integer" %>
<%@ attribute name="end"  required="true"  rtexprvalue="true" type="java.lang.Integer" %>
 
<h1>Tag with body</h1>
 
<c:forEach var="i" begin="${start}" end="${end}" >
    <h2>${i}</h2>
    <c:set var="color" value="${i mod 2 eq 0?'red':'green'}" />
    <p style="border: 1px solid ${color};">
           <jsp:doBody />        
    </p>
</c:forEach>

Теперь пример кода вызывающего данный тег:

<t:HelloTag start="1" end="10">
     <jsp:body>
         Привет, это тело тега
     </jsp:body>
</t:HelloTag>

Внутри тела цикла можно использовать EL выражения, jstl-теги:

<t:HelloTag start="1" end="10">
     <jsp:body>
         Привет, это тело тега
         <c:if test="${1 eq 1}">
                <h3>1 eq 1</h3>
         </c:if>
     </jsp:body>
</t:HelloTag>

Однако, если я запишу выражение на java, то возникнет ошибка. Следующий пример не должен работать, запрещены скриптлеты, выражения, декларации.

<t:HelloTag start="1" end="10">
     <jsp:body>
         Привет, это тело тега
         <h3><%
             out.println(new Date());
         %></h3>
     </jsp:body>
</t:HelloTag>

Это поведение тега в режиме scriptless. А вот если поменять тип тела тега на tagdependent, то можно записать и java-выражение:

<%@ tag body-content="tagdependent" %>

Вот только если запустить код на выполнение, то вы увидите, что формулы не были вычислены: java-выражения были восприняты как текст, просто текст. Итак, в режиме tagdependent вы сами ответственны за интерпретацию тела тега. Честно говоря, такой режим я ни разу не использовал т.к. это излишне усложняет сам тег, но если кто-то поделится живыми примерами с tagdependent, то я буду только благодарен.

В теле тега можно использовать переменные также как мы использовали переменные с fragment-атрибутами. Вот пример исходного кода тега:

<%@ tag body-content="scriptless" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ attribute name="start"  required="true" rtexprvalue="true" type="java.lang.Integer" %>
<%@ attribute name="end"  required="true"  rtexprvalue="true" type="java.lang.Integer" %>
 
<%@ attribute name="cycleVar"  required="true"  rtexprvalue="false" type="java.lang.String" %>
<%@ variable alias="cycleVarAlias"  name-from-attribute="cycleVar" %>
 
<h1>Tag with body</h1>
 
<c:forEach var="cycleVarAlias" begin="${start}" end="${end}" >
    <h2>${cycleVarAlias}</h2>
    <c:set var="color" value="${cycleVarAlias mod 2 eq 0?'red':'green'}" />
    <p style="border: 1px solid ${color};">
      <jsp:doBody />        
    </p>
</c:forEach>

А вот пример файла, откуда тег вызывается.

<t:HelloTag start="1" end="10" cycleVar="i">
     <jsp:body>
        It's Body. Variable "i" is  
         <c:choose>
             <c:when test="${i mod 2 eq 0}">
                 Even
             </c:when>
             <c:otherwise>
                 Odd
             </c:otherwise>
         </c:choose>
     </jsp:body>
</t:HelloTag>


Последнее про что я упомяну это то как подключить подобные улучшенные теги к jspx-документам. Если в обычном jsp-файле я для подключения библиотеки тегов, например, jstl использовал:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

То теперь, в jspx-файле, я должен писать так:

<jsp:root xmlns:c="http://java.sun.com/jsp/jstl/core">
    ... какие-то действия ...
</jsp:root>

Естественно, что старый синтаксис с подключением каталога с тегами, например, так уже не работает.

<%@ taglib tagdir="/WEB-INF/tags" prefix="t" %>

Нужно писать подключение снова через декларацию пространства имен. Я приведу пример абсолютно идентичный предыдущему примеру но олько в стиле jspx:

<jsp:root
        version="2.0"
        xmlns:jsp="http://java.sun.com/JSP/Page"
        xmlns="http://www.w3.org/1999/xhtml"
        xmlns:t="urn:jsptagdir:/WEB-INF/tags"
        xmlns:c="http://java.sun.com/jsp/jstl/core"
        >
 
<jsp:directive.page contentType="text/html; charset=UTF-8" />    
<html>
  <head><title>Simple jspx page</title></head>
  <body>
  <t:HelloTag start="1" end="10" cycleVar="i">
       <jsp:body>
          It's Body. Variable "i" is
           <c:choose>
               <c:when test="${i mod 2 eq 0}">
                   Even
               </c:when>
               <c:otherwise>
                   Odd
               </c:otherwise>
           </c:choose>
       </jsp:body>
   </t:HelloTag>
  </body>
</html>
 
</jsp:root>

На этом про новые "старые" jsp-теги все. Надеюсь, вам понравилось.

Источник — http://black-zorro.com/mediawiki/

Теги: j2ee jsp jstl taglib tags