Книга: Технология XSLT

Выполнение XSLT-преобразований в Java

Выполнение XSLT-преобразований в Java

Язык Java традиционно широко поддерживает XML-технологии: большинство передовых разработок в этой области реализуется, как правило, сначала на Java и уж затем переносится на другие платформы разработки.

Не стал исключением и XSLT. Можно смело сказать, что количество XSLT-средств, написанных на Java, превосходит половину вообще всех существующих в настоящее время XSLT-пакетов.

Для того чтобы продемонстрировать использование XSLT в Java, мы приведем два варианта одной и той же программы — серверного приложения (сервлета), которое по запросу клиента будет возвращать информацию о текущем HTTP-сеансе в формате HTML.

Первый вариант сервлета можно назвать "традиционным". В нем HTML-документ создается серией инструкций out.println(...), которые выводят в выходящий поток размеченную HTML-тегами информацию о текущем сеансе.

Листинг 9.22. Традиционный вариант сервлета

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public class example extends HttpServlet {
 /**
 * Инициализация.
 */
 public void init(ServletConfig config) throws ServletException {
  super.init(config);
 }
 /**
 * Основной метод сервлета
 */
 public void service(HttpServletRequest request,
  HttpServletResponse response)
  throws ServletException, IOException {
  // Выставляем тип содержимого
  response.setContentType("text/html");
  // Инициализируем выходящий поток
  OutputStreamWriter osw =
   new OutputStreamWriter(response.getOutputStream());
  PrintWriter out = new PrintWriter (response.getOutputStream());
  // Выполняем вывод HTML-страницы
  out.println("<html>");
  // Выводим головную часть HTML-документа
  out.println(" <head>");
  out.println("  <title>Request information</title>");
  out.println(" </head>");
  // Выводим тело документа
  out.println(" <body>");
  // Выводим общую информацию о запросе
  out.println("  <h1>General information</h1>");
  out.println("  <table>");
  // Выводим имя сервера
  out.println("   <tr>");
  out.println("    <td>Server name</td>");
  out.println("    <td>" + request.getServerName() + "</td>");
  out.println("   </tr>");
  // Выводим порт сервера
  out.println("   <tr>");
  out.println("    <td>Server port</td>");
  out.println("    <td>" + request.getServerPort() + "</td>");
  out.println("   </tr>");
  // Выводим адрес запрашивающей стороны
  out.println("   <tr>");
  out.println("    <td>Remote address</td>") ;
  out.println("    <td>" + request.getRemoteAddr() + "</td>");
  out.println("   </tr>");
  // Выводим название протокола запроса
  out.println("   <tr>");
  out.println("    <td>Protocol</td>");
  out.println("    <td>" + request.getProtocol() + "</td>");
  out.println("   </tr>");
  // Выводим метод запроса
  out.println("   <tr>") ;
  out.println("    <td>Method</td>");
  out.println("    <td>" + request.getMethod() + "</td>");
  out.println("   </tr>");
  // Выводим URI запроса
  out.println("   <tr>");
  out.println("    <td>Request URI</td>");
  out.println("    <td>" + request.getRequestURI() + "</td>");
  out.println("   </tr>");
  // Выводим строку запроса
  out.println("   <tr>");
  out.println("    <td>Query String</td>");
  out.println("    <td>" + request.getQueryString() + "</td>");
  out.println("   </tr>");
  out.println("  </table>");
  // Выводим параметры запроса
  out.println("  <h1>Request parameters</h1>");
  out.println("  <table>");
  for (Enumeration e = request.getParameterNames();
   e.hasMoreElements();) {
   String name = e.nextElement().toString();
   String[] values = request.getParameterValues(name);
   for (int i=0; i < values.length; i++) {
    out.println("   <tr>");
    out.println("    <td>" + name + "</td>");
    out.println("    <td>" + values[i] + "</td>");
    out.println("   </tr>");
   }
  }
  out.println("  </table>");
  // Выводим параметры HTTP-сессии
  out.println("  <h1>Session parameters</h1>");
  out.println("  <table>");
  HttpSession session = request.getSession(true);
  String[] names = session.getValueNames();
  for (int i=0; i < names.length; i++) {
   String name = session.getValueNames()[i];
   out.println("   <tr>");
   out.println("    <td>" + name + "</td>");
   out.println("    <td>" +
   session.getValue(name).toString() + "</td>");
   out.println("   </tr>");
  }
  out.println("  </table>");
  // Выводим cookies
  response.addCookie(new Cookie("content", "apple jam"));
  out.println("  <h1>Cookies</h1>");
  out.println("  <table>");
  Cookie[] cookies = request.getCookies();
  for (int i=0; i < cookies.length; i++) {
   out.println("   <tr>");
   out.println("    <td>" + cookies[i].getName() + "</td>");
   out.println("    <td>" + cookies[i].getValue() + "</td>");
   out.println("   </tr>");
  }
  out.println("  </table>");
  out.println(" </body>");
  out.println("</html>");
  // Закрываем выходящий поток
  out.close();
 }
}

Результатом обращения к этому сервлету по URL вида

http://localhost/servlet/example?x=1&y=2&z=3&x=4&y=5&z=6

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


Рис. 9.13. Результат обращения к сервлету

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

Второй вариант того же самого сервлета, который мы предложим ниже, демонстрирует, как в данном случае при помощи XSLT можно разделить данные и их презентацию. Идея очень проста: вместо того, чтобы в жестко заданном виде выводить информацию в выходящий поток, можно создать XML-документ в виде DOM-объекта и затем применить к нему XSLT-преобразование, которое создаст для него требуемое HTML-представление.

В этом варианте сервлета мы будем использовать Java-версию XML-библиотеки Oracle XDK (Oracle XML SDK, платформа разработки XML-приложений, созданная в Oracle Corp.). В данном примере из этой библиотеки мы будем использовать только XSLT-процессор (класс XSLProcessor) и реализацию DOM-модели XML-документа (класс XMLDocument). Во всем остальном мы будем полагаться на Java-реализацию стандартных интерфейсов объектной модели документа DOM, разработанной Консорциумом W3. DOM-интерфейсы позволят нам манипулировать XML-документом на уровне модели: создавать и включать друг в друга узлы элементов, текстовые узлы и так далее.

Листинг 9.23. Вариант сервлета, использующий XSLT

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import java.net.*;
import oracle.xml.parser.v2.*;
import org.w3c.dom.*;
public class example extends HttpServlet {
 /**
 * Функция, создающая в элементе parent элемент с именем name и
 * текстовым значением value. Если value имеет значение null,
 * текст не создается.
 */
 public static Element addElement(Element parent, String name, String value) {
  Element child = parent.getOwnerDocument().createElement(name);
  parent.appendChild(child);
  if (value != null) {
   Text text = parent.getOwnerDocument().createTextNode(value);
   child.appendChild(text);
  }
  return child;
 }
 /**
 * Инициализация.
 */
 public void init(ServletConfig config) throws ServletException {
  super.init(config);
 }
 /**
 * Основной метод сервлета
 */
 public void service(HttpServletRequest request,
  HttpServletResponse response)
  throws ServletException, IOException {
  // Выставляем тип содержимого
  response.setContentType("text/html");
  // Инициализируем выходящий поток
  OutputStreamWriter o_sw =
   new OutputStreamWriter(response.getOutputStream());
  PrintWriter out = new PrintWriter(response.getOutputStream());
  // Получаем объекты
  cookie Cookie[] cookies = request.getCookies();
  // Создаем выходящий документ
  XMLDocument doc = new XMLDocument();
  // Создаем корневой элемент
  Request Element elRequest = doc.createElement("Request");
  doc.appendChild(elRequest);
  // Создаем элемент General
  Element elGeneral = addElement(elRequest, "General", null);
  // Создаем элементы, содержащие общую информацию
  addElement(elGeneral, "ServerName", request.getServerName());
  addElement(elGeneral, "ServerPort",
   Integer.toString(request.getServerPort()));
  addElement(elGeneral, "RemoteAddr", request.getRemoteAddr());
  addElement(elGeneral, "Protocol", request.getProtocol());
  addElement(elGeneral, "Method", request.getMethod());
  addElement(elGeneral, "RequestURI", request.getRequestURI());
  addElement(elGeneral, "QueryString", request.getQueryString());
  // Создаем элемент Param
  Element elParam = addElement(elRequest, "Param", null);
  // В элементе Param создаем элементы, описывающие параметры запроса
  for (Enumeration e = request.getParameterNames();
   e.hasMoreElements();) {
   String name = e.nextElement().toString();
   String[] values = request.getParameterValues(name);
   // Для каждого из значений каждого из параметров
   // создаем соответствующий элемент
   for (int i=0; i < values.length; i++)
    addElement(elParam, name, values[i]);
  }
  // Создаем элемент Session
  Element elSession = addElement(elRequest, "Session", null);
  // Получаем объект HTTP-сессии
  HttpSession session = request.getSession(true);
  // Получаем имена параметров сессии
  String[] names = session.getValueNames();
  // В элементе Session создаем по элементу
  // для каждого из параметров сессии
  for (int i=0; i < names.length; i++)
   addElement(elSession, session.getValueNames()[i],
    session.getValue(session.getValueNames()[i]).toString());
  // Создаем элемент Cookie
  Element elCookie = addElement(elRequest, "Cookie", null);
  // Создаем по элементу для каждого из объектов cookies
  for (int i=0; i < cookies.length; i++)
   addElement(elCookie, cookies[i].getName(), cookies[i].getValue());
  // Преобразовываем созданный документ и выводим результат
  try {
   // Загружаем преобразование
   XSLStylesheet stylesheet = new XSLStylesheet(
    new URL("http://localhost/stylesheet.xsl"), null);
   // Выполняем преобразование
   XMLDocumentFragment fragment =
    (XMLDocumentFragment)doc.transformNode(stylesheet);
   // Выводим результат
   fragment.print(out);
  }
  catch (MalformedURLException mue) {}
  catch (XSLException xsle) {}
  // Закрываем выходящий поток
  out.close();
 }
}

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

Листинг 9.24. XML-документ, сгенерированный в сервлете

<Request>
 <General>
  <ServerName>aphrodite.fzi.de</ServerName>
  <ServerPort>80</ServerPort>
  <RemoteAddr>127.0.0.1</RemoteAddr>
  <Protocol>HTTP/1.1</Protocol>
  <Method>GET</Method>
  <RequestURI>/servlet/example1</RequestURI>
  <QueryString>x=1&amp;y=2&amp;z=3&amp;x=4&amp;y=5&amp;z=6
  </QueryString>
 </General>
 <Param>
  <z>3</z>
  <z>6</z>
  <y>2</y>
  <y>5</y>
  <x>1</x>
  <x>4</x>
 </Param>
 <Session>
  <v>4</v>
 </Session>
 <Cookie>
  <content>apple jam</content>
  <JServSessionIdroot>aaenbyjqc0</JServSessionIdroot>
 </Cookie>
</Request>

После того как генерация документа завершена, к нему применяется преобразование stylesheet.xsl, которое создает его HTML-представление.

Листинг 9.25. Преобразование stylesheet.xsl

<xsl:stylesheet
 version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:template match="Request">
  <html>
   <head>
    <title>Request information</title>
   </head>
   <body><xsl:apply-templates mode="table"/></body>
  </html>
 </xsl:template>
 <xsl:template match="*" mode="table">
  <h1><xsl:apply-templates select="." mode="header"/></h1>
  <table><xsl:apply-templates mode="row"/></table>
 </xsl:template>
 <xsl:template match="General" mode="header">
  <xsl:text>General information</xsl:text>
 </xsl:template>
 <xsl:template match="Param" mode="header">
  <xsl:text>Request parameters</xsl:text>
 </xsl:template>
 <xsl:template match="Session" mode="header">
  <xsl:text>Session parameters</xsl:text>
 </xsl:template>
 <xsl:template match="Cookie" mode="header">
  <xsl:text>Cookies</xsl:text>
 </xsl:template>
 <xsl:template match="*" mode="row">
  <tr>
   <td><xsl:apply-templates select="." mode="name"/></td>
   <td><xsl:value-of select="."/></td>
  </tr>
 </xsl:template>
 <xsl:template match="*" mode="name">
  <xsl:value-of select="name()"/>
 </xsl:template>
 <xsl:template match="General/ServerName" mode="name">
  <xsl:text>Server name</xsl:text>
 </xsl:template>
 <xsl:template match="General/ServerPort" mode="name">
  <xsl:text>Server port</xsl:text>
 </xsl:template>
 <xsl:template match="General/RemoteAddr" mode="name">
  <xsl:text>Remote address</xsl:text>
 </xsl:template>
 <xsl:template match="General/RequestURI" mode="name">
  <xsl:text>Request URI</xsl:text>
 </xsl:template>
 <xsl:template match="General/QueryString" mode="name">
  <xsl:text>Query string</xsl:text>
 </xsl:template>
</xsl:stylesheet>

Результатом этого преобразования является следующий HTML-документ, внешний вид которого полностью идентичен документу, показанному на рис. 9.13.

Листинг 9.26. Результирующий HTML-документ

<html>
 <head>
  <title>Request information</title>
 </head>
 <body>
  <h1>General information</h1>
  <table>
   <tr>
    <td>Server name</td>
    <td>aphrodite.fzi.de</td>
   </tr>
   <tr>
    <td>Server port</td>
    <td>80</td>
   </tr>
   <tr>
    <td>Remote address</td>
    <td>127.0.0.1</td>
   </tr>
   <tr>
    <td>Protocol</td>
    <td>HTTP/1.1</td>
   </tr>
   <tr>
    <td>Method</td>
    <td>GET</td>
   </tr>
   <tr>
    <td>Request URI</td>
    <td>/servlet/example1</td>
   </tr>
   <tr>
    <td>Query string</td>
    <td>x=1&amp;y=2&amp;z=3&amp;x=4&amp;y=5&amp;z=6</td>
   </tr>
  </table>
  <h1>Request parameters</h1>
  <table>
   <tr>
    <td>z</td>
    <td>3</td>
   </tr>
   <tr>
    <td>z</td>
    <td>6</td>
   </tr>
   <tr>
    <td>y</td>
    <td>2</td>
   </tr>
   <tr>
    <td>y</td>
    <td>5</td>
    </tr>
   <tr>
    <td>x</td>
    <td>1</td>
   </tr>
   <tr>
    <td>x</td>
    <td>4</td>
   </tr>
  </table>
  <h1>Session parameters</h1>
  <table>
   <tr>
    <td>v</td>
    <td>4</td>
   </tr>
  </table>
  <h1>Cookies</h1>
  <table>
   <tr>
    <td>content</td>
    <td>apple jam</td>
   </tr>
   <tr>
    <td>JServSessionIdroot</td>
    <td>aaenbyjqc0</td>
   </tr>
  </table>
 </body>
</html>

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

Представим, к примеру, что нам потребовалось перевести названия полей выводимого документа на русский язык — получить текст "Общая информация" вместо "General information" и так далее. В первом случае для внесения этого элементарного представления потребуется переписывать, перекомпилировать и обновлять на сервере сервлет; во втором случае все, что нужно будет сделать, — это исправить несколько строк в файле stylesheet.xsl.

Оглавление книги


Генерация: 0.995. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз