Статьи

Использование RSS Reader: ROME, Spring MVC, Embedded Jetty

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

Генеральная

Недавно я создал образец веб-приложения, которое выполняет функцию чтения RSS. Я хотел изучить РИМ для чтения RSS. Я также хотел создать приложение, используя Spring контейнер и MVC для простейшего представления. Для быстрой разработки я использовал Jetty в качестве сервера, используя для этого простой Java-класс.
Весь код можно найти на GitHub, eyalgo / rss-reader .

содержание

  1. Maven Зависимости
  2. Jetty Server
  3. Зависимость весны
  4. Spring MVC
  5. РИМ

Maven Зависимости

Сначала я не мог получить правильную версию Jetty для использования. Один с групповым идентификатором, а другой — по затмению. После тщательного изучения, проб и ошибок я взял библиотеку затмения. Весна просто стандартная. Я нашел ROME с новейшей версией под GutHub. Это все еще SNAPSHOT.

Вот список зависимостей:

  • весна
  • мол
  • Рим и Рим-сборщик
  • logback и slf4j
  • Для тестирования
    • Junit
    • Mockito
    • Hamcrest
    • весна-тест

Jetty Server

Несколько лет назад я работал с Wicket Framework и познакомился с Jetty и его простым использованием для создания сервера. Я решил пойти в этом направлении и пропустить стандартный веб-сервер, работающий с развертыванием WAR.

Существует несколько способов создания сервера Jetty. Я решил создать сервер, используя контекст веб-приложения.

Сначала создайте контекст:

1
2
3
4
5
6
private WebAppContext createContext() {
  WebAppContext webAppContext = new WebAppContext();
  webAppContext.setContextPath("/");
  webAppContext.setWar(WEB_APP_ROOT);
  return webAppContext;
}

Затем создайте сервер и добавьте контекст в качестве обработчика:

1
2
Server server = new Server(port);
  server.setHandler(webAppContext);

Наконец, запустите сервер:

1
2
3
4
5
6
try {
    server.start();
  } catch (Exception e) {
    LOGGER.error("Failed to start server", e);
    throw new RuntimeException();
  }

Все находится по адресу https://github.com/eyalgo/rss-reader/tree/master/src/test/java/com/eyalgo/rssreader/server .

Весенняя структура проекта

RSS Reader Структура проекта

RSS Reader Структура проекта

Зависимость весны

В web.xml я объявляю application-context.xml и web-context.xml. В web-context.xml я говорю, что Spring должен был сканировать компоненты: <context:component-scan base-package="com.eyalgo.rssreader"/> .
В application-context.xml я добавляю bean-компонент, который является внешним классом, и поэтому я не могу его сканировать (используйте аннотации):
<bean id="fetcher" class="org.rometools.fetcher.impl.HttpURLFeedFetcher"/> .

Помимо сканирования, я добавляю правильную аннотацию в правильные классы.
@Repository
@Service
@Controller
@Autowired

Spring MVC

Для того, чтобы иметь некоторое представление о RSS-каналах (и атомах), я использовал простые страницы MVC и JSP. Чтобы создать контроллер, мне нужно было добавить @Controller для класса. Я добавил @RequestMapping("/rss") поэтому все запросы должны иметь префикс rss . Каждый метод имеет объявление @RequestMapping . Я решил, что все ПОЛУЧИТЬ .

Добавление параметра в запрос

Просто добавьте @RequestParam("feedUrl") перед параметром метода.

Перенаправление запроса

После добавления местоположения RSS я хотел перенаправить ответ, чтобы показать все текущие элементы RSS. Таким образом, метод добавления RSS-канала необходим для возврата строки. Возвращаемое значение: «redirect: all» .

1
2
3
4
5
@RequestMapping(value = "feed", method = RequestMethod.GET)
  public String addFeed(@RequestParam("feedUrl") String feedUrl) {
    feedReciever.addFeed(feedUrl);
    return "redirect:all";
  }

Вернуть класс ModelAndView

В Spring MVC, когда метод возвращает String, платформа ищет JSP-страницу с этим именем. Если его нет, мы получим ошибку. (Если вы хотите вернуть только строку, вы можете добавить @ResponseBody в метод.)

Чтобы использовать ModelAndView , вам нужно создать его с именем: ModelAndView modelAndView = new ModelAndView("rssItems");
Имя сообщит Spring MVC, на какую JSP ссылаться. В этом примере он будет искать rssItems.jsp .

Затем вы можете добавить в ModelAndView «объекты»:

1
2
3
List<FeedItem> items = itemsRetriever.get();
  ModelAndView modelAndView = new ModelAndView("rssItems");
  modelAndView.addObject("items", items);

На странице JSP вам нужно ссылаться на имена добавленных вами объектов. И тогда вы можете получить доступ к их свойствам. Итак, в этом примере у нас будет следующее в rssItems.jsp :

1
2
3
4
5
6
<c:forEach items="${items}" var="item">
    <div>
      <a href="${item.link}" target="_blank">${item.title}</a><br>
        ${item.publishedDate}
    </div>
  </c:forEach>

Запись

Spring «знает», как добавить jsp в качестве суффикса к имени ModelAndView, потому что я объявил его в web-context.xml. В компоненте класса: org.springframework.web.servlet.view.InternalResourceViewResolver . Установив префикс, этот бин также сообщает Spring, что нужно искать страницы jsp. Пожалуйста, посмотрите:

Обработка ошибок

Существует несколько способов обработки ошибок в Spring MVC. Я выбрал общий способ, при котором для любой ошибки будет отображаться страница с общей ошибкой. Сначала добавьте @ControllerAdvice в класс, который вы хотите обрабатывать ошибки. Во-вторых, создайте метод для каждого типа исключения, которое вы хотите перехватить. Вам нужно аннотировать метод с помощью @ExceptionHandler . Параметр сообщает, какое исключение будет обрабатывать этот метод. У вас может быть метод для IllegalArgumentException и другой для другого исключения и так далее. Возвращаемое значение может быть любым, и оно будет действовать как обычный контроллер. Это означает, что jsp (например) с именем объекта, который возвращает метод. В этом примере метод перехватывает все исключения и активирует error.jsp , добавляя сообщение на страницу.

1
2
3
4
5
6
@ExceptionHandler(Exception.class)
  public ModelAndView handleAllException(Exception e) {
    ModelAndView model = new ModelAndView("error");
    model.addObject("message", e.getMessage());
    return model;
  }

РИМ

ROME — это простая в использовании библиотека для обработки RSS-каналов: https://github.com/rometools/rome .
rome-fetcher — это дополнительная библиотека, которая помогает получать (извлекать) RSS-каналы из внешних источников, таких как HTTP или URL: https://github.com/rometools/rome-fetcher

На данный момент последняя сборка — 2.0.0-SNAPSHOT.

Пример того, как читать входной XML-файл RSS, можно найти по адресу: https://github.com/eyalgo/rss-reader/blob/master/src/test/java/com/eyalgo/rssreader/runners/MetadataFeedRunner. Ява

Чтобы облегчить жизнь, я использовал Рим-сборщик. Это дает вам возможность указать URL (канал RSS) и исключить из него весь SyndFeed . При желании вы можете добавить кэширование, чтобы оно не загружало кэшированные элементы (элементы, которые уже были загружены). Все, что вам нужно, это создать сборщик с параметром FeedFetcherCache в конструкторе.

Использование:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
@Override
  public List<FeedItem> extractItems(String feedUrl) {
    try {
      List<FeedItem> result = Lists.newLinkedList();
      URL url = new URL(feedUrl);
      SyndFeed feed = fetcher.retrieveFeed(url);
      List<SyndEntry> entries = feed.getEntries();
      for (SyndEntry entry : entries) {
        result.add(new FeedItem(entry.getTitle(), entry.getLink(), entry.getPublishedDate()));
      }
      return result;
    } catch (IllegalArgumentException | IOException | FeedException | FetcherException e) {
      throw new RuntimeException("Error getting feed from " + feedUrl, e);
    }
}

Запись

Если вы получите предупреждающее сообщение (выглядит как System.out), в котором говорится, что fetcher.properties отсутствует, просто добавьте пустой файл под ресурсами (или в корень пути к классам).

Резюме

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