Статьи

Использование пользовательских тегов для объединения RSS-каналов в веб-приложения на основе JSP

С обилием постоянно растущих сайтов новостей и блогов (Weblog), отслеживание происходящего может быть сложной задачей. К счастью, такие стандарты, как RSS (Really Simple Syndication), обеспечивают простой способ получения контента с определенного сайта и его объединения в приложение для чтения новостей. Это означает, что вместо того, чтобы искать новости самостоятельно, читатель новостей отслеживает интересующие вас сайты и загружает новый контент по мере его публикации.

Это отличная модель, и многие люди перенесли эту концепцию в Интернет, агрегируя другой контент на своих веб-сайтах и ​​предлагая услуги агрегации через Интернет. В этой статье я покажу вам, как использовать пользовательские теги JSP для реализации этого типа функциональности в качестве повторно используемого компонента в ваших собственных веб-приложениях на основе JSP. Хотя я предполагаю, что вы знакомы с Java, и у вас есть некоторые практические знания по созданию веб-приложений с помощью JavaServer Pages (JSP), я не буду полагаться на знание пользовательских тегов JSP. Прежде чем начать, загрузите код, который мы будем использовать в этой статье здесь .

Агрегаторы RSS и новостей — краткая история

Стандарт RSS существует уже несколько лет, но только недавно он начал завоевывать популярность. Одна из причин этого заключается в том, что такие программы, как MovableType и Radio Userland, сделали блог доступным для широких масс в доступной упаковке. Если ранее новостные разделы веб-сайтов содержали снимки конкретной компании или отдельного лица и редко обновлялись, эти RSS-инструменты позволяют нам применять более динамичный подход, предоставляя простой механизм добавления новых новостей на сайты.

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

В результате появился RSS, формат действительно простой синдикации . По сути, RSS — это просто XML-документ, который можно использовать для описания содержимого, доступного на данном веб-сайте. Как правило, «контент» означает новости, но другие виды использования RSS включают в себя краткие статьи, короткие рассказы и так далее. Хорошим примером RSS-канала являются британские новости от BBC . Внедрение стандартного формата RSS значительно упростило агрегацию контента.

Чтение RSS-каналов с Java

Поскольку RSS-каналы представляют собой не что иное, как стандартизированные XML-документы, чтение и обработка RSS довольно просты на любом языке, поддерживающем XML. Теперь, когда J2SE 1.4 обеспечивает интегральную поддержку XML, это просто вопрос использования соответствующих классов для чтения в документе XML. После прочтения документа его представление пользователю в настольном или веб-приложении становится тривиальным.

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

Прежде чем говорить больше о пользовательских тегах, давайте быстро посмотрим, как мы будем читать в RSS-каналах с использованием кода Java. Манипулирование XML-документами в коде Java может быть утомительным, и поэтому вместо того, чтобы кодировать против исходного XML, я решил создать очень простое объектное представление RSS-канала. Первый класс называется RssFeed , и он представляет собой заданный канал RSS, который содержит несколько элементов.

 package rss;   import java.util.ArrayList;  import java.util.Collection;  import java.util.Collections;   public class RssFeed {   private Collection items = new ArrayList();   public void addItem(RssItem item) {    items.add(item);  }   public Collection getItems() {    return Collections.unmodifiableCollection(items);  }   } 

На самом деле RSS-каналы имеют другие характеристики, но для наших целей этого достаточно. Далее следует класс Java, который представляет элемент в RSS-канале. Как правило, несколько элементов информации будут представлены для каждого элемента в ленте; Я решил обернуть их в объект. Информация, относящаяся к каждому элементу в этом примере, — это его заголовок и ссылка (URL) на полный текст в Интернете.

 package rss;   public class RssItem {   private String title;  private String link;   public String getTitle() {    return title;  }   public void setTitle(String title) {    this.title = title;  }   public String getLink() {    return link;  }   public void setLink(String link) {    this.link = link;  }   } 

Последняя часть головоломки — это класс, который на самом деле будет читать XML-документ и преобразовывать его в наше объектное представление — класс RssReader . Вместо того, чтобы увязнуть в синтаксисе и семантике чтения XML-файлов, реализация этого класса была опущена. По сути, все, что делает метод read() , — это доступ к RSS-каналу по указанному URL-адресу и преобразование каждого элемента, содержащегося в фиде, в объект RssItem .

 package rss;   public class RssReader {   /**   * Reads the RSS feed at the specified URL and returns an RssFeed instance   * representing it.   */  public RssFeed read(String url) {    ... body of method omitted ...  }   } 

Это вся логика, необходимая для чтения RSS-каналов. Давайте теперь посмотрим, как подключить это к странице JSP.

Чтение RSS-каналов из JSP

Для целей этой статьи я собираюсь предположить, что вы решили создать свое веб-приложение с использованием веб-технологий Java и, в частности, что вы собираетесь использовать JavaServer Pages (JSP). Одна из замечательных особенностей этой технологии заключается в том, что она упрощает внедрение динамического поведения в ваши страницы — все, что вам нужно сделать, это добавить немного Java здесь и там. Например, мы могли бы использовать классы, которые мы только что создали на странице JSP:

 <%  RssReader reader = new RssReader();  RssFeed rssFeed = reader.read("http://www.acme.com/rss.xml");  Iterator it = rssFeed.getItems().iterator();  while (it.hasNext()) {    RssItem rssItem = (RssItem)it.next();  %>    <a href="<%= rssItem.getLink() %>"><%= rssItem.getTitle() %></a>    <br />  <%  }  %> 

Этот код получает RSS-канал с указанного URL-адреса и, используя стандартный класс java.util.Iterator , циклически перебирает каждый элемент, отображая гиперссылку на весь материал.

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

Оборачивание RSS Reader в пользовательский тег JSP

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

Основное различие между пользовательскими тегами и обычными классами Java заключается в том, как они используются на страницах JSP. Там, где код Java просто внедряется в страницу, пользовательские теги используются с синтаксисом XML. Например, вот как будет использоваться пользовательский тег, когда мы закончим его создание. Обратите внимание, что здесь у нас есть начальный тег, некоторое содержимое тела, а затем конечный тег.

 <rss:rssFeed url="http://www.acme.com/rss.xml">  <a href="<%= rssItem.getLink() %>"><%= rssItem.getTitle() %></a>  <br />  </rss:rssFeed> 

Это даст тот же результат, что и код скриптлета, который мы видели ранее — тег обеспечивает итерацию для каждого элемента в ленте. По сути, тег теперь является конструкцией цикла, и содержание тела тега оценивается для каждой итерации, так же, как содержимое цикла while оценивается для каждой итерации. Пользовательские теги дают нам возможность создавать более чистые и краткие страницы JSP, которые получают преимущества от повторного использования компонентов — повышенную удобство обслуживания, качество, надежность и т. Д.

Создание класса обработчика тегов

Теперь, когда вы понимаете, почему пользовательские теги полезны, давайте посмотрим, как их создать. Как я уже говорил, поведение заключено в обычный класс Java, который реализует определенный интерфейс. В случае пользовательских тегов интерфейс, который должен быть реализован (как минимум), это Tag из пакета javax.servlet.jsp.tagext . Этот интерфейс довольно прост и предоставляет ряд методов обратного вызова, которые будут выполняться при использовании тега на странице JSP. Во время выполнения страница JSP создает для данного пользовательского тега новый экземпляр класса обработчика тегов и вызывает методы обратного вызова. Поначалу это может показаться сложным, но это становится довольно простым, если вы создали для себя пару тегов.

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

 package tagext;   import java.util.Collection;  import java.util.Iterator;   import javax.servlet.jsp.JspException;  import javax.servlet.jsp.PageContext;  import javax.servlet.jsp.tagext.TagSupport;   import rss.RssFeed;  import rss.RssReader;   public class RssFeedTag extends TagSupport {   private String url;  private Iterator iterator; 

В примере использования тега, который я представил ранее, мы увидели, что есть начальный тег, некоторый контент тела и конечный тег. Эти различные аспекты тега важны, потому что они определяют, когда страница JSP будет запускать методы обратного вызова, которые мы пишем в нашем классе обработчика тегов. Функциональность, которую мы должны реализовать, идентична показанной ранее; нам нужно прочитать RSS-канал с URL-адреса, а затем выполнить итерацию по каждому элементу в канале, чтобы можно было создать гиперссылку. Для класса TagSupport доступны три метода обратного вызова: doStartTag() , doAfterBody() и doEndTag() . Давайте посмотрим на каждый из них по очереди.

Метод doStartTag() вызывается, когда на странице JSP встречается начальный тег, и вызывается только один раз для любого заданного пользовательского тега.

   public int doStartTag() throws JspException {    RssReader reader = new RssReader();    RssFeed feed = reader.read(url);    iterator = feed.getItems().iterator();     if (iterator.hasNext()) {      pageContext.setAttribute("rssItem", iterator.next());      return EVAL_BODY_INCLUDE;    } else {      return SKIP_BODY;    }  } 

Поскольку этот код вызывается только один раз для каждого тега и является первым из вызванных методов обратного вызова, именно здесь мы можем прочитать RSS-канал и настроить итератор для циклического перемещения по коллекции элементов. Если вы думаете об этом методе как о начале цикла while, первым шагом является проверка наличия элементов в коллекции. Если таковые имеются, мы хотим сделать первый элемент доступным для использования на странице JSP, чтобы мы могли использовать его в теле содержимого тега. Для этого мы устанавливаем атрибут PageContext для ссылки на первый элемент в коллекции. Далее, мы должны сообщить странице JSP, что содержимое тела тега должно быть оценено, что достигается путем возврата постоянного значения EVAL_BODY_INCLUDE . Однако, если в коллекции нет элементов, мы сообщаем странице JSP, что она должна пропустить оценку содержимого тела, возвращая постоянное значение SKIP_BODY .

Следующим интересным методом обратного вызова является метод doAfterBody() , который вызывается после оценки содержимого тела тега.

   public int doAfterBody() throws JspException {    if (iterator.hasNext()) {      pageContext.setAttribute("rssItem", iterator.next());      return EVAL_BODY_AGAIN;    } else {      return SKIP_BODY;    }  } 

После оценки содержимого тела следующим шагом является проверка того, есть ли еще какие-либо элементы в коллекции. Если таковые имеются, мы хотим сделать новый элемент доступным для страницы JSP и указать, что содержание тела следует переоценить, возвращая постоянное значение EVAL_BODY_AGAIN . После этого снова вызывается метод doAfterBody() , чтобы узнать, требуется ли еще одна оценка. Эта последовательность повторяется до тех пор, пока в коллекции больше не будет элементов, и в этом случае возвращается постоянное значение SKIP_BODY .

Когда все оценки выполнены, выполняется последний метод обратного вызова.

   public int doEndTag() throws JspException {    return EVAL_PAGE;  } 

Эта реализация просто сообщает странице JSP, что остальная часть страницы должна быть обработана как обычно. Фактически, реализация класса TagSupport содержит ту же реализацию этого метода, что означает, что нам не нужно его реализовывать. Тем не менее, я показал это здесь для полноты.

Последний метод, который нам нужно реализовать, это установщик для URL. Если вы вернетесь к примеру использования тега на странице, вы заметите, что URL-адрес RSS-канала указан в качестве атрибута пользовательского тега. Чтобы сделать эту информацию доступной для класса обработчика тегов, нам нужно написать метод установки так же, как мы бы писали методы установки для свойств JavaBean. Любые методы установки, которые соответствуют атрибутам пользовательского тега, вызываются перед методами обратного вызова, так что их значения доступны для использования в этих методах обратного вызова.

   public void setUrl(String url) {    this.url = url;  }   } 

Компиляция класса обработчика тегов

Чтобы скомпилировать класс обработчика тега, убедитесь, что в вашем пути к классам есть классы Servlet и JSP. Если вы используете Tomcat 4, их можно найти в $TOMCAT_HOME/common/lib/servlet.jar . Полученный файл классов должен быть помещен в каталог WEB-INF / classes вашего веб-приложения в соответствующей структуре каталога / пакета.

Описание тега

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

 <?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/dtd/web-jsptaglibrary_1_2.dtd"> 

Далее у нас есть начало определения библиотеки тегов. Хотя пользовательские теги можно использовать повторно, они должны быть определены в контексте библиотеки тегов — набора из одного или нескольких тегов, которые обычно связаны каким-либо образом. Этот блок XML позволяет нам определить версию нашей библиотеки тегов, требуемую версию JSP, краткое имя и описание библиотеки тегов.

 <taglib>   <tlib-version>1.0</tlib-version>  <jsp-version>1.2</jsp-version>  <short-name>rss</short-name>  <description>    Tags used to present RSS information.  </description> 

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

   <tag>    <name>rssFeed</name>    <tag-class>tagext.RssFeedTag</tag-class>    <body-content>JSP</body-content>    <description>      A tag to present a headlines/titles of items from an RSS feed.    </description> 

Из них содержание тела, вероятно, нуждается в дополнительном объяснении. Спецификация JSP предоставляет несколько типов содержимого тела, которые могут быть определены для пользовательских тегов, два наиболее полезных из которых — пустой и JSP. Пустой тип содержимого тела указывает, что пользовательский тег будет использоваться без содержимого тела на странице, что удобно, когда вы просто хотите, чтобы тег выполнял какое-то действие. С другой стороны, тип содержимого тела JSP указывает, что между начальными и конечными тегами будут использоваться обычные конструкции JSP. Это то, что мы используем в этом примере, потому что мы хотели бы, чтобы содержание тела оценивалось для каждого элемента в RSS-ленте.

Следующая часть XML-файла описывает переменную сценария, которая будет введена на страницу в теле содержимого тега. В коде обработчика тега мы получаем следующий элемент RSS из коллекции, а затем помещаем ссылку на этот объект в контекст страницы под именем rssItem. Пользовательские теги могут сделать эти атрибуты доступными в виде переменных сценариев на странице JSP, чтобы к ним можно было обращаться с помощью синтаксиса выражения времени запроса <%= ... %> . Здесь мы указываем имя и тип переменной вместе с областью действия NESTED чтобы указать, что переменная должна быть доступна только между начальным и конечным тегами.

     <variable>      <name-given>rssItem</name-given>      <variable-class>rss.RssItem</variable-class>      <scope>NESTED</scope>    </variable> 

Последний аспект тега для описания — это его атрибуты. В этом примере есть только один атрибут, называемый url , который используется для указания источника RSS-канала. Чтобы убедиться, что тег работает должным образом, мы указали, что этот атрибут должен быть указан при использовании тега. Элемент rtexprvalue тега attribute говорит, что значение атрибута должно быть статически определено на странице JSP. Другими словами, значение атрибута не является результатом выражения времени запроса.

     <attribute>      <name>url</name>      <required>true</required>      <rtexprvalue>false</rtexprvalue>    </attribute>  </tag>   </taglib> 
Использование тега

Для целей этого примера давайте предположим, что файл TLD был сохранен как rss.tld в rss.tld WEB-INF вашего веб-приложения. Чтобы использовать пользовательский тег, сначала необходимо указать странице JSP, где найти описание этого тега. Это достигается с помощью директивы taglib: атрибут uri указывает на файл TLD, представляющий библиотеку тегов, а атрибут prefix указывает, как теги в этой библиотеке тегов будут идентифицироваться и использоваться на странице. Затем, используя тот же синтаксис, что и раньше, мы можем использовать тег для чтения RSS-канала, предоставленного любым веб-сайтом, и генерировать набор гиперссылок на текущие новости на этом сайте.

 <%@ taglib uri="/WEB-INF/rss.tld" prefix="rss" %>   <rss:rssItems url="http://www.sitepoint.com/rss.php">  <a href="<%= rssItem.getLink() %>"><%= rssItem.getTitle() %></a>  <br />  </rss:rssItems> 
Будущие улучшения

Представленный здесь тег довольно прост в своей реализации, и в него можно внести множество улучшений. Например, каждый раз, когда запрашивается страница JSP, тег открывает HTTP-соединение для получения содержимого RSS-канала. Хотя это нормально для сайта с низким трафиком, лучшим решением будет кэширование канала на регулярной основе. Это позволит избежать снижения производительности, связанного с открытием сетевого подключения для каждого запроса страницы.

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

Резюме

В этой статье мы рассмотрели, что такое RSS, как читать RSS-каналы и как интегрировать эти функции в веб-приложение на основе JSP. Хотя мы могли бы встроить эту функциональность непосредственно в страницу JSP с помощью скриптлетов Java-кода, разработка пользовательского тега JSP позволила нам создать более поддерживаемый компонент с дополнительным преимуществом, которое также можно использовать повторно.

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