Продолжается подписка на наши издания! Вы не забыли подписаться?

Подходы к созданию шаблонов web-страниц
в серверных Java-приложениях

Петр Михеев
peter-miheev@yandex.ru

Сегодня не так просто найти в интернете сайт, состоящий исключительно из статических HTML-страниц. Практически повсеместно применяются технологии динамического создания страниц, такие, как CGI, PHP, ASP, JSP и другие.

Перечисленные технологии имеют одну большую общую проблему – смешение содержания и представления, то есть информационного наполнения, обычно предоставляемого тем или иным источником данных (БД, корпоративные приложения или просто файлы, хранящие контент) и внешнего оформления – форматирующего HTML-кода, стилей (CSS) и так далее. Это затрудняет развитие проекта и порождает новую профессию дизайнера-программиста, который должен одновременно создавать и программное обеспечение, и дизайн страницы. Еще худшая ситуация складывается, если дизайнеры и программисты, создавая сайт, не могут работать параллельно и вступают друг с другом в конфликт. Чаще всего программистам приходится включать “куски” HTML непосредственно в код, а дизайнеры практически устраняются от процесса разработки. Без переделки кода, и иногда очень значительной, изменить представление информации в полученных решениях практически невозможно. С другой стороны, создание такого представления также является серьезной работой – приходится методом проб и ошибок (т.е. путем правки программного кода и просмотра результата в браузере) создавать подходящее оформление. Все это приводит к появлению неприглядных страниц.

Для решения этих проблем применяются различные технологии шаблонов web-страниц. Шаблоны появились еще на заре разработки для web. Тогда они представляли “заготовки” HTML-кода, из которых путем манипуляций в HTML-редакторе получались готовые страницы. Сегодня такими заготовками манипулируют уже не дизайнеры в своих редакторах, а серверные web-приложения. Таким образом, сегодня шаблон web-страницы представляет собой блок HTML, который, благодаря специальным тегам или внедренным сценариям, облегчает включение динамически сгенерированного содержания на этапе выполнения. При использовании шаблонов программистам необходим некоторый стандартизированный интерфейс для работы с ними – шаблонный движок (в английском языке существует устоявшийся термин – template engine), который может иметь и некоторые дополнительные функции, например, поддерживать кэширование шаблонов, их динамическое обновление, и так далее.

Сегодня существует большое количество подходов к созданию шаблонов web-страниц и множество шаблонных движков, написанных на всевозможных языках программирования и предназначенных для использования на различных серверных платформах. В этой статье мы рассмотрим несколько диаметрально противоположенных подходов к разработке шаблонов – от специальных “языков шаблонов” до “чистого” HTML, который автоматически преобразуется в класс Java. Между этими двумя крайними подходами или несколько в стороне стоят технология Java Server Pages, использующая пользовательские теги, и XSLT-преобразования. Для каждого типа шаблонов мы подробно проанализируем соответствующий шаблонный движок. Все они работают на платформе J2EE, которая является одной из самых популярных серверных платформ для создания крупных программных проектов. Именно о решениях на базе Java-технологий и пойдет речь в этой статье.

Традиционная разработка
web-приложений в J2EE

В свое время компания Sun Microsystems представила публике технологию сервлетов. Сервлеты получили широкое признание разработчиков web-приложений и позволили практически полностью устранить основные недостатки традиционного CGI-программирования. В отличие от последнего, сервлеты используют легковесные потоки Java, а не отдельный процесс, который создается в CGI для каждого запроса. Программист, использующий сервлеты, может применять практически все библиотеки языка Java (конечно, кроме тех, которые участвуют в создании пользовательского интерфейса), в том числе, обеспечивающие унифицированный доступ к различным источникам данных. Эти преимущества позволили сервлетам войти в разряд ключевых технологий разработки серверных приложений. Все последующие серверные Java-технологии от Sun были основаны именно на них. Но, хотя никто не подвергает сомнению эффективность использования сервлетов, им также присуще смешение кода и представления. Традиционный подход к созданию HTML-кода, который используется в сервлетах (фактически это вызов функции out.println()), вызывает целое множество проблем, затронутых в самом начале статьи.

Следующим важным этапом в построении серверных приложений стала технология Java Server Pages (JSP). Это был ответ Sun на технологию Active Server Pages (ASP), разработанную Microsoft. JSP и ASP – сходные технологии: обе добавляют к HTML язык сценариев и способность обращаться к внешним компонентам. Используя JSP, разработчики могут создавать шаблоны web-страниц, помещая Java-код внутри HTML-страниц, а на сервер возлагается ответственность за автоматическое создание сервлетов на основе таких страниц. По сути, JSP-страница – это тот же сервлет, но там не нужно использовать функцию out.println(), чтобы вывести содержимое. HTML-код может быть включен в JSP-страницу непосредственно, без использования out.println(), и дизайнеры страниц могут вносить изменения в HTML без серьезного риска испортить Java-код. Но художники и дизайнеры, работающие над HTML-файлами, далеки от совершенства (а программисты – тем более ), и наличие Java-кода внутри HTML приводит практически к таким же неуклюжим решениям, как и наличие HTML внутри Java-программ. Обычно программисты используют весь “арсенал” средств Java внутри JSP-страниц. В итоге JSP-код становится смесью HTML и кода Java, трудным для понимания, отладки, и поддержки.

Чтобы избежать подобных проблем, разработчики стали использовать Java Beans, которые содержали бизнес-логику, необходимую JSP-странице. Подход, известный как архитектура Model-1, был рекомендован в версии 0.91 спецификации JSP. Он предполагает делегирование бизнес-функций компонентам bean, что делает приложения, созданные по этому принципу, более устойчивыми к изменениям. Архитектура Model-1, показанная на рисунке 1, включает JSP-документы, Java Beans и бизнес-объекты.

Согласно данной архитектуре, запросы передаются JSP-документам, которые взаимодействуют с бизнес-объектами через Java Beans. Такое опосредованное обращение уменьшает зависимость JSP-документов от изменений, вносимых в бизнес-объекты. Это важно, поскольку в больших проектах модификация бизнес-объектов производится достаточно часто. Если интерфейс, реализуемый Java Beans, не изменяется, модификация бизнес-объектов не оказывает влияния на JSP-документы.

Бизнес-объекты и Java Beans реализуются разработчиками программного обеспечения, а web-дизайнеры отвечают за создание JSP-документов. В идеале такое разделение обязанностей должно было бы обеспечить возможность параллельной работы сотрудникам различных специальностей, участвующих в выполнении проекта. Однако на практике реализовать одновременное выполнение работ достаточно сложно, поскольку JSP-документы не только предоставляют содержимое, но и генерируют его, а для этого необходимо создавать Java-код.

Следующий недостаток Model-1 заключается в том, что она предполагает передачу запроса непосредственно представлению, т.е. JSP-странице. Но что, если одному запросу могут соответствовать несколько различных представлений? Например, запрос к базе данных может возвратить страницу с результатами запроса или сообщение об ошибке доступа к базе данных. Этот же запрос может породить несколько ответов на различных языках – это довольно типичная ситуация. В результате появилась идея передавать запрос некоторому общему компоненту сервера, который определяет представление (т.е. страницу JSP) для каждого конкретного запроса.

Согласно архитектуре Model-2, запросы передаются сервлету, который обращается к бизнес-объектам и создает содержимое. Это содержимое сохраняется в компоненте (bean-е), к которому имеет доступ JSP-страница. Она отображает содержимое, применяя для этого, как правило, средства HTML (вместо HTML может применяться любое другое представление – от текстовых файлов до голосовых сообщений).

По сути, Model-2 представляет собой традиционную архитектуру Model-View-Controller (MVC), где бизнес-объекты соответствуют модели (представляют данные), сервлет является контроллером (обрабатывает запросы), а JSP-страницы играют роль представления. Благодаря разделению бизнес-логики и средств представления, такая архитектура позволяет легко заменять компоненты и обеспечивает создание гибкого программного обеспечения. Подход Model-2 решает ряд проблем, возникающих при разработке web-приложений, но это далеко не панацея от всех бед. Обратная сторона Model-2 – это ее сложность. Применять ее следует далеко не везде, но, тем не менее, на сегодня эта самая популярная модель web-приложений.

Классическая архитектура Model-2 предлагает использовать в качестве представления (т.е. шаблонов web-страниц, которые должны создавать дизайнеры) главным образом JSP-страницы. Рассмотрим основные недостатки такого подхода и альтернативные решения проблемы представления в web-приложениях, которые предлагаются в различных шаблонных движках, построенных на платформе J2EE.

Недостатки Java Server Pages

Несмотря на огромную популярность технологии JSP, это – не единственный вариант создания шаблонов web-страниц. То, что JSP поставляется в составе платформы J2EE, еще не значит, что надо использовать именно ее. Это утверждение звучит банально, но некоторые разработчики пытаются использовать JSP (или EJB, или JDO) только потому, что считают, что их приложение не будет настоящим приложением J2EE, если их не использовать. Обычно платформы содержат куда больше различных технологий, чем это необходимо для разработки конкретного приложения. Если использование какой-либо технологии порождает проблемы, то пользоваться ей совсем не обязательно. Стоит внимательно посмотреть на все ее достоинства и недостатки, а уже затем решать – использовать данную технологию или нет. Технология Java Server Pages не лишена некоторых недостатков. Давайте посмотрим на некоторые из них.

Во-первых, разработчики JSP-страниц часто поддаются искушению поместить бизнес-код прямо внутрь страницы. Хотя это и является плохим стилем, большинство разработчиков поступают именно так. Частично это компенсируется использованием пользовательских тегов (их мы обсудим ниже). При использовании JSP разработчики могут разделить содержание и представление, но ничто не обязывает их так поступать. До тех пор, пока JSP позволяют вставлять код внутрь страниц, удобнее (особенно когда не хватает времени) делать последние изменения в самом коде, чем писать специальный пользовательский тег для этого. Здесь просматривается аналогия со столь популярным сегодня языком Java. В Java запрещено многое из порождавшего большие проблемы в C и C++. Конечно, теоретически ни один “хороший программист” не строит свой код преимущественно на глобальных переменных в C и не вставляет бизнес-логику внутрь JSP-страниц, но на практике бывает и по другому. Java популярен сегодня именно потому, что он гарантирует, что многие поспешные решения никогда не будут воплощены в жизнь. А страницы JSP с этой точки зрения очень похожи на язык С, который позволяет вести некачественную разработку.

Во-вторых, выполнение самых простых вещей в JSP может потребовать далеко не тривиальных знаний от web-дизайнеров. Например, дизайнеру web-страницы нужно получить значение Cookie. Для этого в JSP-странице нужно написать

<% String mycookie = ServletUtils.getCookie(request, "mycookie"); %>

Получается, что даже самые простые задачи слишком сложны для дизайнеров, и они не могут выполнять их, не обладая навыками Java-программирования. В приведенном выше примере такое знание необходимо даже для того, чтобы просто получить значение Cookie. То же самое относится к ветвлению и выполнению итераций по спискам объектов.

В-третьих, ошибки в JSP-страницах могут вызывать удивительно непонятные и бесполезные сообщения об ошибках. Это происходит из-за того, что JSP-страница преобразуется в сервлет, а уже затем компилируется в байт-код. Таким образом, в сообщении об ошибке содержится информация об ошибке выполнения в классе, который был автоматически сгенерирован по JSP-странице. По такой информации только примерно можно понять, что же произошло на самом деле.

Шаблонные движки

Столкнувшись с такими проблемами, разработчики стали искать пути их решения. Самым удачным и очевидным решением стал переход c JSP на другие, новые технологии создания шаблонов web-страниц. Эти технологии создавались с таким расчетом, чтобы использовать по максимуму все преимущества JSP, но избавиться от ее недостатков.

На сегодняшний день существует множество разных подходов к разработке шаблонов web-страниц. Однако в любом случае, у них есть ряд общих свойств:

Для использования таких шаблонов в серверных приложениях служат специальные программные компоненты, или шаблонные движки. Типичная архитектура web-приложения, использующего шаблонный движок, представлена на рисунке 3.

Дизайнеры создают и редактируют шаблоны web-страниц, представляющие собой HTML-документы, в которые необходимые данные включается динамически на этапе выполнения. В этом и заключается основная функция шаблонного движка – включать содержание на этапе выполнения. Эта же характеристика является основным критерием классификации шаблонных движков. Существует несколько различных подходов к разработке шаблонов:

  1. Первый и самый распространенный подход заключается в размещении логики включения содержания непосредственно в шаблоне. При этом используется специальный язык шаблонов, который позволяет шаблонам произвольным образом обращаться к объектам бизнес-логики, и даже обрабатывать их, чтобы сформировать необходимое представление. Такой подход реализован в целой серии шаблонных движков: WebMacro, Velocity, FreeMarker, Tea. Здесь мы рассмотрим шаблонный движок Velocity, который доступен по адресу http://jakarta.apache.org/velocity. Как вы уже догадались (по адресу), этот движок разрабатывается в рамках проекта Apache – Jakarta, он хорошо интегрирован с большинством других продуктов Apache, и можно предположить, что он скоро займет в своей группе доминирующее положение.

  2. Следующий подход абсолютно иной. Он заключается в компиляции простых файлов HTML в файлы Java-классов и создании отдельного класса для каждого шаблона HTML; каждый класс содержит представление документа в виде DOM. Логика включения содержания содержится в компоненте, который выдает необходимую страницу в ответ на запрос, например в сервлете. Этот подход на сегодняшний день реализован только в одном шаблонном движке – XMLC, который является частью сервера приложений Enhydra. XMLC доступен по адресу http://xmlc.enhydra.org.

  3. Как мы уже обсуждали, страницы JSP, содержащие всю бизнес-логику, с трудом могут рассматриваться в качестве шаблонов web-страниц. Однако благодаря возможности создания пользовательских тегов, они создают серьезную конкуренцию остальным шаблонным движкам. Что такое пользовательские теги, и как их использовать, мы обсудим далее.

  4. Последний подход – это использование XSLT-шаблонов. Признанным лидером в этой области является Apache Cocoon, который основывается на XML и XSLT-преобразованях. Cocoon также разрабатывается в рамках проекта Apache – XML Project, он доступен по адресу http://xml.apache.org/cocoon.

Перейдем к рассмотрению конкретных шаблонных движков. Для каждого шаблонного движка будет приведен пример создания шаблона и его практического применения. Чтобы не отвлекаться на принципы использования баз данных и хранилищ объектов (все-таки в реальном приложении данные хранятся именно там), предположим, что динамическое содержание представляет собой текущее время (что еще меняется так динамично и постоянно!).

Jakarta Velocity

В рамках проекта Jakarta Velocity разработчики создали минимальную инфраструктуру для создания web-приложений с использованием шаблонов. Эта инфраструктура облегчает сопоставление запросов и шаблонов, а также обеспечивает страницы на базе этих шаблонов информацией о пользовательской сессии и любыми данными бизнес-объектов, которые они должны использовать. Первая возможность реализуется введением в состав пакета Velocity класса VelocityServlet, который облегчает генерацию web-страниц на основе шаблонов. Такая архитектура соответствует архитектуре Model-View-Controller. Но основная особенность Velocity – это поддержка простого языка создания сценариев (с синтаксисом, похожим на Perl). Разработчики Velocity называют его "языком шаблонов", отличая его от развитого языка создания сценариев. Некоторые утверждают, что скрипты Velocity проще изучать и отлаживать, чем страницы JSP. Данный язык поддерживает:

Наиболее простой способ использования шаблонов Velocity в Web-приложении – создание подкласса класса VelocityServlet и переопределение метода handleRequest(), который получает в качестве параметров объекты Context (Context представляет собой набор пар имя-значение, где значением может быть любой объект Java, и служит для передачи данных между контроллером и шаблоном), HTTPServletRequest и HTTPServletResponse. Результат работы метода handleRequest() – объект Template, который выводит на экран необходимое представление. Пример контроллера может выглядеть следующим образом:

import java.io.*;
import java.util.*;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.velocity.Template;
import org.apache.velocity.context.Context;
import org.apache.velocity.servlet.VelocityServlet;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.ParseErrorException;

public class SampleServlet extends VelocityServlet
{
  protected Properties loadConfiguration(ServletConfig config)
    throws IOException, FileNotFoundException
  {
    // получение файла свойств и его загрузка
    String propsFile = config.getInitParameter(INIT_PROPS_KEY);
    Properties p = new Properties();
    if (propsFile != null)
    {
      String realPath = getServletContext().getRealPath(propsFile);
      if (realPath != null) 
      { 
        propsFile = realPath;
      }
      p.load(new FileInputStream(propsFile)); 
    }

    // установка log-файла
    String log = p.getProperty(Velocity.RUNTIME_LOG);
    if (log != null)
    {
      log = getServletContext().getRealPath(log);
      if (log != null) 
      { 
        p.setProperty(Velocity.RUNTIME_LOG, log);
      } 
    }
     
    // установка загрузчика шаблонов
    String path = p.getProperty(Velocity.FILE_RESOURCE_LOADER_PATH);
    if (path != null)
    {
      path = getServletContext().getRealPath(path);
      if (path != null) 
      {
        p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, path); 
      }
    }
    return p;
  }  
 
  public Template handleRequest(HttpServletRequest request, 
    HttpServletResponse response, Context ctx)
  {
    ctx.put("time", (new java.util.Date()).toString());
    Template outty = null;
    try
    { 
      outty =  getTemplate("time.vm"); 
    }
    catch(ParseErrorException pee)
    { 
      System.out.println("SampleServlet: parse error " + pee); 
    }
    catch(ResourceNotFoundException rnfe)
    { 
      System.out.println("SampleServlet : template not found " + rnfe); 
    }
    catch(Exception e)
    {
      System.out.println("Error " + e); 
    }

    return outty;
  }
}

Первый метод в этом примере (loadConfiguration()) служит для установки параметров среды исполнения Velocity. Он загружает свойства среды исполнения, устанавливает параметры загрузчика шаблонов, а также определяет log-файл. В документации по Velocity очень туманно описано, как установить среду исполнения. Поэтому перед ее установкой стоит посмотреть довольно хорошее руководство, которое расположено по адресу http://www. cv64.org/computing/installation/velocity/installation.html.

Второй метод (handleRequest()) в типичном web-приложении выполняет следующее:

В нашем примере мы просто помещаем текущее время в контекст и возвращаем шаблон time.vm, который выглядит так:

<html>
  <head><title>Velocity page</title></head>
  <body>
    Hello from velocity! <b>$time</b>
  </body>
</html>

Нетрудно догадаться (даже не изучая Velocity), что он выводит значение ключа time, содержащего текущее время. Результат выполнения данного шаблона показан на рисунке 4.

Рисунок 4.

Таким образом, Velocity позволяет полностью удалить HTML из Java-кода и использовать серверные сценарии в HTML-файлах. При этом файлы HTML преобразуются в файлы шаблонов Velocity. Это делает HTML более сложным, и слегка сокращает возможности использования инструментов верстки HTML. Дополнительные накладные расходы для авторов HTML компенсируются увеличением производительности труда программистов в результате снятия с них бремени верстки HTML. Введение языка шаблонов усложняет изучение Velocity Web-дизайнерами. Однако масштабы этой проблемы не столь велики, поскольку язык шаблонов довольно прост. Вместе с тем он обладает достаточными возможностями, чтобы поддержать любое кодирование, необходимое в шаблонах.

Enhydra XMLC

Velocity внедряет сценарии определенного вида внутрь HTML-файлов. Подход XMLC абсолютно иной. XMLC – это Java-компилятор для HTML- (или XML-) файлов шаблонов. На самом деле, XMLC компилирует простые файлы HTML в файлы Java-классов, создавая отдельный класс для каждого шаблона HTML. XMLC решает задачу включения содержания в шаблон, используя HTML-атрибут ID (атрибуты ID – это идентификаторы, уникальные в пределах документа) в сочетании с Объектной Моделью Документа XML (Document Object Model – DOM). Каждый скомпилированный Java-класс содержит представление документа в виде DOM. Классы имеют методы доступа для всех элементов HTML с атрибутами ID. Код Java, управляющий документами, использует методы доступа для изменения шаблона, добавляя или изменяя соответствующие элементы. XMLC использует элемент SPAN для вставки или изменения текста и атрибут CLASS для группировки наборов элементов.

Пусть дизайнер хочет вывести текущее время. Для этого он создает следующий шаблон:

<html>
<head> 
<title> Local time! </title> 
</head>
<body>
  Hi, the local time is <span id="time">now</span>.
</body
</html>

Следующий шаг состоит в компиляции шаблона в класс Java. Поскольку в дистрибутив XMLC не входит командный файл для компиляции шаблонов в операционных системах Microsoft (только для Unix), то можно использовать командный файл, подобный следующему.

set XMLCPATH=./xmlc.jar;./gnu-regexp-1.1.4.jar;./jtidy-r7-xmlc.jar;./wireless.jar;./xerces-1.4.4-xmlc.jar;./xhtml.jar
set CLSPATH=%XMLCPATH%
java -classpath %CLSPATH% org.enhydra.xml.xmlc.commands.xmlc.XMLC -classpath %CLSPATH% -keep %1

После компиляции шаблона получается класс, реализующий интерфейс org.enhydra.xml.xmlc.html.HTMLObject и расширяющий класс org.enhydra.xml.xmlc.html.HTMLObjectImpl. Для нас важны некоторые изменяющие шаблон методы, которые содержатся в этом классе.

. . .
public class Sample 
  extends org.enhydra.xml.xmlc.html.HTMLObjectImpl 
  implements org.enhydra.xml.xmlc.XMLObject, 
    org.enhydra.xml.xmlc.html.HTMLObject
{
  private org.enhydra.xml.lazydom.html.LazyHTMLElement $element_Time;
. . .
  public org.w3c.dom.html.HTMLElement getElementTime() 
  {
    if (($element_Time == null) && ($elementId_time >= 0)) 
    {
      $element_Time = (org.enhydra.xml.lazydom.html.LazyHTMLElement)
        fLazyDocument.getNodeById($elementId_time);
    }

    return $element_Time;
  }
...
  public void setTextTime(String text)
  {
    if (($element_Time == null) && ($elementId_time >= 0))
    {
      $element_Time = (org.enhydra.xml.lazydom.html.LazyHTMLElement)
        fLazyDocument.getNodeById($elementId_time);
    }
    doSetText($element_Time, text);
  }
. . .
}

После того, как класс шаблона создан, нужно включить его в web-приложение, построенное согласно архитектуре MVC. Ниже приводится код сервлета-контроллера:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.w3c.dom.html.*;

public class XMLCExample extends HttpServlet 
{
  public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException 
  {
    Sample sample = new Sample();
    // Change time within the <SPAN> tags.
    String now = (new java.util.Date()).toString();
    sample.setTextTime(now);

    // Print out the results.
    PrintWriter out = response.getWriter();
    out.print(sample.toDocument());
  }
}

Этот сервлет выполняет следующие шаги:

Результат представлен на рисунке 5.

Рисунок 5.

Главным преимуществом XMLC является то, что страницы HTML остаются инструментом макетирования сайта. Дизайнер продолжает разрабатывать страницы по мере необходимости, и модифицированные шаблоны передаются программисту без потери информации, необходимой для генерации динамических страниц. Это происходит совершенно естественным образом, поскольку страницы HTML никогда не содержат ничего, кроме правильных тегов HTML. HTML-страницы, используемые дизайнером при создании макета, станут шаблонами представления, и будут иметь силу на всех стадиях разработки проекта. Это также означает, что дизайнеры могут довольно долго работать независимо. Они отдают очередной крупный фрагмент макета программистам только тогда, когда он их удовлетворяет. Это также позволяет использовать в полную силу инструменты верстки HTML. К недостаткам XMLC можно отнести то, что даже незначительные изменения в файлах HTML требуют перекомпиляции. Но XMLC имеет механизм авто-компиляции, позволяющий сконфигурировать приложение так, чтобы всегда использовать самые последние версии шаблонов без необходимости перезапуска.

Страницы JSP, расширенные пользовательскими тегами

До появления спецификации JSP 1.1 (выпущенной в начале 2000 года) использованию Java-кода в JSP-страницах альтернативы не было. Страницы JSP, содержащие весь этот код, с трудом могут рассматриваться в качестве "шаблонов" web-страниц – они фактически представляют собой файлы исходных текстов логики представления. Версия 1.1 предоставила мощную возможность расширения системы тегов разметки (tag extension), которая позволяет определять пользовательские теги и связывать их с классами Java. Используя дополнительные теги, разработчики могут перемещать бизнес-код в классы-обработчики этих тегов. Можно также использовать JSP в качестве шаблонного механизма, разрабатывая наборы тегов, обработчики которых содержат весь код Java, который иначе пришлось бы включать в страницы JSP. Это не устраняет возможности использования большого количества кода на странице, но почти полностью устраняет потребность в этом.

Наличие пользовательских тегов позволяет организовать работу так, чтобы дизайнеры и программисты могли решать свои задачи практически независимо друг от друга. При этом дизайнеры web-страниц ориентируются на использование HTML-, XML- и JSP-тегов, разработчики программного обеспечения концентрируют свое внимание на реализации низкоуровневых средств, предназначенных, например, для доступа к базам данных и бизнес-объектам. Результаты работы программистов становятся доступными авторам web-страниц в виде набора тегов.

После выпуска JSP 1.1 многие производители серверов приложений начали поставлять обширные библиотеки дополнительных тегов вместе с серверами (например, BEA WebLogic и Allaire JRUN). Библиотека тегов – это набор связанных тегов. Библиотека тегов состоит из Tag Library Descriptor (TLD), который является XML документом, описывающим теги библиотеки, и обработчиков тегов – handler’ов, реализующих функциональность библиотеки тегов. Tag handler – это bean, который реализует функциональность отдельного тега. TLD описывает для каждого тега реализующий его Tag handler.

В следующем листинге показан JSP-документ, в котором применяется простейший пользовательский тег. В нем отсутствуют атрибуты и тело тега.

<html>
<head>
<title> Пользовательский тег, выводящий текущее время. </title>
<body>
<%@ taglib uri="/WEB-INF/jsp/time.tld" 
           prefix="tcs"%>
Hello! It's <tcs:time/> now.
</body>
</html>

Данный тег просто выводит текущее время.

В предыдущем листинге присутствует директива taglib, в которой указывается путь к XML-описанию библиотеки тегов (TLD – tag library descriptor). Файл, содержащий описание библиотеки тегов, также имеет расширение .tld. В директиве taglib указывается также атрибут prefix, который задает префикс при обращении к тегам данной библиотеки. В примере атрибут prefix имеет значение tcs, поэтому для обращения к дескриптору time используется выражение <tcs:time/>.

Рисунок 7.

Как уже было сказано, описание библиотеки тегов представляет собой XML-документ, в котором определены библиотека и составляющие ее теги. TLD-файл для нашего примера приводится в следующем листинге. JSP-документ, выводящий текущее время, использует именно это описание.

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
        PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
               "http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd">

<taglib>

  <tlib-version>1.0</tlib-version>
  <jsp-version>1.1</jsp-version>
  <short-name>Time Tag Library</short-name>

  <tag>
    <name>time</name>
    <tag-class>tcs.ShowTime</tag-class>
  </tag>       

</taglib>

Вторая строка этого документа содержит тип документа (taglib) и задает URL определения этого типа (Document Type Definition, DTD). DTD используется генераторами сервлетов для проверки корректности документа.

Элемент taglib уточняет определение библиотеки. В вышеприведенном листинге задана версия библиотеки 1.0. и указано, что она может использоваться средствами обработки JSP 1.1 или более поздней версии. Кроме этого, задается имя библиотеки.

Теги определяются с помощью элемента tag, в состав которого входят два обязательных параметра name и tagclass. Элемент tagclass задает класс Java, реализующий пользовательский тег. Этот класс называется классом поддержки тега. В данном случае в описании должно быть указано, что тело тега отсутствует. Поэтому если внутрь этого тега включить какое-либо содержание, будет считаться, что он задан некорректно.

Класс поддержки дескриптора реализует интерфейс Tag, входящий в пакет javax.servlet.jsp.tagext. В этом интерфейсе объявлены 6 методов; последние три из них используются наиболее часто. Контейнер сервлетов вызывает методы интерфейса Tag в следующем порядке. Методы doStartTag и doEndTag вызываются соответственно при встрече открывающего и закрывающего тега. Каждый из методов возвращает целочисленное значение, которые определяют поведение контейнера сервлетов после завершения методов. После вызова метода doEndTag контейнер вызывает метод release, чтобы освободит ресурсы, которые использовались классом поддержки тега.

package tcs;

import java.util.*;
import javax.servlet.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

import java.io.*;

/**
 * Display current time.
 */
public class ShowTime extends TagSupport 
{
  
  public int doEndTag() throws JspException 
  {
    JspWriter out = pageContext.getOut();
    try 
    {
      out.println(new Date().toLocaleString());
    } 
    catch (IOException ex) 
    {
      throw new JspTagException("IOException: "+ex.toString());
    }
      return SKIP_BODY;
    }
}

Подобно многим классам поддержки пользовательских тегов, класс ShowTime реализован как подкласс TagSupport. Этот класс реализует интерфейс Tag, кроме того, он содержит ряд вспомогательных методов.

Таким образом, пользовательские теги реализуются достаточно просто. Подводя итог вышесказанному, выделим основные шаги создания пользовательского тега. Чтобы использовать свой собственный тег при разработке JSP-страниц, нужно включить в этот документ директиву taglib. А также создать описание библиотеки дескрипторов (.tld файл) и реализовать класс поддержки тега как подкласс класса TagSupport и переопределить в нем методы doStartTag и doEndTag.

По мере развития технологии JSP возможность создания пользовательских тегов становится одним из основных преимуществ данной технологии. Сегодня в состав JSP входит стандартная библиотека тегов. Используя библиотеки тегов, разработчики могут формировать интерактивные сайты с большим объемом динамического содержания на базе JSP-страниц, не содержащих Java-кода вообще, либо использующих его в минимальном количестве. При этом стоит учесть, что при развитии спецификаций JSP большое внимание уделяется обеспечению обратной совместимости. Последние версии спецификаций JSP позволяют встраивать код внутрь страниц, поэтому можно ожидать, что эта возможность будет использоваться в течение определенного времени. Научиться использовать библиотеки пользовательских тегов не так просто, но это все-таки предпочтительнее, чем создание файлов, содержащих и HTML, и код на развитом языке программирования – поскольку такие "страницы" вынуждают дизайнера программировать, или программиста заниматься разметкой и дизайном.

XSLT-шаблоны на примере Apache Cocoon

XSLT (еXtensible Stylesheet Language Transformations – на русский язык переводится как “расширяемый язык стилей для преобразований”) в последнее время стал очень популярной XML-технологией. Спецификация определяет XSLT как язык для преобразований одних XML-документов в другие XML-документы. Но за время своего существования XSLT давно вышел за рамки своей начальной задачи. Теперь уместнее согласиться с редактором новой версии языка Майклом Кеем (Michael Kay) в том, что XSLT – это язык для преобразования структуры документов. В любом случае, в преобразовании участвуют два документа: входящий документ, который подвергается преобразованию, и документ, который описывает само преобразование. Результатом преобразования является исходящий документ. Описание XSLT-преобразования – это XML-документ, описывающий набор правил, в соответствии с которым трансформируется входящий документ. Выполнением преобразований над документами занимаются специальные программы, которые называются XSLT-процессорами. Упрощенно схема преобразований представлена на следующем рисунке.

Процессор получает входящий документ и, применяя правила преобразования, генерирует исходящий документ. В частности, правила преобразования могут определять, каким образом отобразить на web-станице данные, содержащиеся во входящем XML-документе.

Сегодня спецификация XSLT вышла на уровень, позволяющий ее эффективное использование. Начали появляться web-приложения, использующие XSLT-процессоры. Эти приложения динамически генерируют XML-документы на стороне сервера. К этому содержанию на сервере (иногда на клиенте) применяются XSLT-преобразования, в результате чего получаются HTML-страницы, отображаемые броузерами.

Рисунок 8. Схема XSLT-преобразования.

Рисунок 9. Архитектура Apache Cocoon.

Достаточно популярными и распространенными приложениями, использующими XSLT-процессоры для публикации информации, являются Cocoon (входящий в Apache XML Project) и XSQL от Oracle. В этой статье мы рассмотрим Cocoon, который свободно распространяется, он доступен по адресу http://xml.apache.org/cocoon.

Apache Cocoon – это один из мощнейших продуктов для публикации информации в web. Он полностью написан на Java и хорошо интегрирован с сервисами J2EE. Как и другие подобные системы, он использует XSLT-преобразования на стороне сервера. Типовой сценарий доступа к данным в этом случае описывается следующей схемой:

Чтобы получить такой документ, Cocoon использует конвейеры, которые состоят из трех типов базовых компонентов:

Подобная архитектура показана на рисунке 9.

Давайте посмотрим, как вывести текущее время с помощью Cocoon. При этом предполагается, что вначале динамически создается XML-документ, содержащий текущее время и выбранное приветствие. Затем создается необходимое логическое представление с помощью XSLT-преобразования (в данном случае используется преобразование в формат Scalable Vector Graphics Standard – Стандарт Масштабируемой Векторной Графики, который основан на XML и специально предназначен для поддержки векторной графики web-браузерами). И, наконец, создается физическое представление результата преобразования, в нашем случае это рисунок в формате JPEG.

Принятый в Cocoon подход позволяет дизайнерам применять самые разнообразные виды представления информации пользователю – JPEG, GIF, PNG (в случае использования рисунков); PDF, PostScript, Word (для использования текстовых документов); HTML, XHTML, XML (для представления текстовой информации в web). Cocoon автоматически создаст нужное представление – для этого в него включено большое количество сериализаторов.

Итак, чтобы вывести текущее время, нужно определить конвейер, который будет вызываться при обращении к файлу time.jpeg. Ниже приводится описание конвейера, которое нужно добавить в …\webapps\cocoon\sitemap.xmap.

<map:match pattern="time.jpeg">
  <map:generate 
    src="docs/samples/time-page.xsp"
    type="serverpages"/>
  <map:transform
    src="logicsheets/page2svg.xsl"/>
  <map:serialize type="svg2jpeg"/>
</map:match>

Как уже говорилось, конвейер обычно состоит из трех компонентов: генератора, XSLT-процессора и сериализатора. Мы напишем только генератор и XSLT-преобразование, а в качестве сериализатора будем использовать встроенный svg2jpeg.

Для создания генераторов в Cocoon используется концепция XML Server Pages (XSPs). Страницы XSP представляют обычные XML-документы, дополненные вставками кода Java, который содержится внутри тегов <xsp:logic> или <xsp:expr>. Существует возможность вынести такие вставки из XML-документов в отдельные библиотеки, называемые logicsheets, напоминающие библиотеки пользовательских тегов в JSP. XSP-файл, используемый в нашем примере, приводится ниже.

<?xml version="1.0" encoding="ISO-8859-1"?>

<xsp:page language="java" xmlns:xsp="http://apache.org/xsp">
<page>
  <title>XSP Page that shows current time ...</title>
    <content>
      <greeting> Hello! </greeting>  
      <time>  <xsp:expr>new Date().toLocaleString()</xsp:expr></time>
    </content>
</page>
</xsp:page>

В этой статье мы не будем подробно останавливаться на языке XSLT и формат векторной графики SVG. О них есть достаточно информации, в том числе и в Internet. Поэтому просто приведем соответствующее XSLT-преобразование.

<?xml version="1.0"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:template match="page">
  <svg width="550" height="160">
   <defs>
    <filter id="blur1"><feGaussianBlur stdDeviation="3"/></filter>
    <filter id="blur2"><feGaussianBlur stdDeviation="1"/></filter>
   </defs>
   
   <g title="this is a tooltip">
    <rect style="fill:#0086B3;stroke:#000000;
                 stroke-width:4;filter:url(#blur1);"
          x="30" y="30" rx="20" ry="20" width="500" height="80"/>
    <xsl:apply-templates/>
   </g>
  </svg>
 </xsl:template>
 
 <xsl:template match="greeting">
  <text style="fill:#FFFFFF;font-size:24;
        font-family:TrebuchetMS-Bold;filter:url(#blur2);" x="65" y="80">
    <xsl:value-of select="."/>
  </text>
 </xsl:template>
 <xsl:template match="time">
  <text style="fill:#FFFFFF;font-size:24;font-family:TrebuchetMS-Bold;
               filter:url(#blur2);" x="165" y="80">
    It's <xsl:value-of select="."/> now.
  </text>
 </xsl:template>

</xsl:stylesheet>

Результат работы показан на рисунке 10.

Рисунок 10. Рисунок JPEG, содержащий текущее время.

Системы, построенные на связке XML-XSLT, гораздо более масштабируемы, чем использующие другие упомянутые в статье технологии. Кроме того, они позволяют использовать XSL для выдачи информации различным типам клиентов. Один и тот же XML-документ можно использовать (применяя различные преобразования) для представления информации web-броузерам, мобильным телефонам, PDA и другим устройствам. Сегодня производительность XML-парсеров и XSL-процессоров достигла приемлемого уровня, так что преобразования XML в HTML на сервере не создает критических узких мест в реальных приложениях.

Однако разработка XSLT-преобразований требует некоторого уровня программистского опыта у дизайнеров. XSLT-преобразования включают код, который фильтрует, сортирует, и форматирует данные исходного XML-документа. Поэтому для Web-дизайнера довольно сложно перейти к программированию XSLT.

Из положительных моментов следует отметить, что использование XSLT приводит к чистому разделению бизнес-логики и логики представления, так что разработчики могут разрабатывать компоненты для управления и обновления модели независимо от компонентов представления (преобразований XSLT).

Заключение

Шаблоны web-страниц – простая, но полезная концепция, которая должна быть в наборе каждого разработчика web-приложений. Шаблоны инкапсулируют компоновку и тем самым минимизируют усилия по её изменению. Кроме того, шаблоны могут предоставлять различное содержание в разных случаях, основываясь на ролях пользователей, и могут быть вложенными в другие шаблоны. Если вы планируете применять шаблоны web-страниц, стоит рассмотреть как можно больше альтернативных технологий их построения, прежде чем делать выбор. Эта статья не рекомендует использовать какую-либо определенную технологию построения шаблонов. Каждая из упомянутых здесь технологий хороша для определенного круга задач. Оглянитесь вокруг, найдите то, что наиболее подходит для вас, и сделайте правильный выбор.

Разумеется, часто требуется просто быстро и качественно сделать небольшой сайт для своей организации и обеспечить его динамическое наполнение, а не заниматься разработкой и продвижением некоторого глобального программного продукта. В этом случае вполне возможно использование кода, встроенного в JSP, хотя бы просто для ускорения и облегчения разработки. Это вряд ли вызовет серьезные проблемы, если, разумеется, ваш небольшой сайт не начнет расти неожиданно быстро. Хотя конечно, если есть время и возможность, а самое главное – желание, лучше делать все как можно более качественно.


Copyright © 1994-2016 ООО "К-Пресс"