Как стать автором
Обновить

Ratings Service / Online-сервис + REST API для поиска рейтингов фильмов

Время на прочтение6 мин
Количество просмотров1.5K
Ratings Service — это online-сервис, который позволяет узнать рейтинг фильма по его названию.

image

Отличительные особенности:
  1. поиск по нескольким Интернет-базам одновременно (на данный момент это КиноПоиск.Ru и КиноКопилка.ру)
  2. легкий интерфейс; можно пользоваться с мобильного телефона, например, через Opera Mini
  3. возможность получать результаты выборки в формате XML в стиле REST
  4. хостинг на Google App Engine / Java
  5. Open Source

Я бы хотел рассказать о некоторых особенностях реализации и поделиться впечатлениями работы с Google App Engine / Java. Ниже вы также найдете адрес проекта на Google Code, с выложенными исходными кодами.

На написание сервиса у меня ушло три дня и это было действительно весело. Весело, в основном, потому, что в GAE/Java многое не работает, и некоторые вещи пришлось реализовывать самому практически с нуля, например, написать простенький движок для XPath запросов :)

Но обо всем по порядку.

Средства разработки


Согласно Getting Started Guide из IDE на данный момент официально поддерживается только Eclipse, для этого есть Google Eclipse Plugin. Причем три дня назад была доступна только версия для Ganymede/3.4 (я пользовался ей), сегодня уже доступна версия для Galileo/3.5.

Создание проекта GAE Web Application ничем не сложнее, чем создание обычного Java-проекта в Eclipse. Запуск и отладка приложения работает отлично на локальной машине. В добавок к этому deploy одной кнопкой делает разработку просто приятной.

Еще очень понравилась приятная мелочь, что файл log4j.properties оказался уже в проекте и настроенный как надо :) Все что мне осталось — это добавить в classpath jar log4j и включить отладку для моего кода:

log4j.category.dmitrygusev.ratings=DEBUG, A1


Из отрицательных моментов (не знаю куда это отнести — скорее к Eclipse), на третий день перестал работать deploy (GAE Issue #1235), но это быстро удалось исправить.

Еще один отрицательный момент — это то, что структура каталогов проекта в GAE Web Application отличается от структуры каталогов Maven2. Понятно, что можно настроить Maven2-проект под Google App Engine, но хотелось бы увидеть поддержку Maven2 прямо в Google Eclipse Plugin.

Frameworks & Libraries


Что касается Framework'ов и сторонних библиотек, то большинство из них, которые действительно оказались бы полезными, не работают в GAE/Java, в основном, из-за того, что Google частично закрыли доступ к Reflection API, а также другие API, могущие повлечь за собой security-риски.

Больше всего в этом проекте не хватало поддержки XML-фреймворков (хотя бы JAXB) и XPath. То стандартное Java XML API (DOM, SAX, StAX?) которое поддерживается и которым я раньше запросто пользовался, сейчас кажется не очень user friendly.

В итоге я решил остановиться на библиотечке NanoXML Lite, которая хорошо себя показала в проекте мобильного клиента для online-сервиса www.4konverta.com.

Всё API NanoXML Lite состоит из одного класса XMLElement. Работать с ним очень просто, например:

XMLElement xml = new XMLElement();
xml.parseString("<xml attr='value'>hello world</xml>");

xml.getChildren(); // Возвращает детей
xml.getAttribute("attr"); // Возвращает значение атрибута, и т.д.


Но есть два недостатка, которые пришлось пофиксить:
  1. изначально NanoXML Lite не поддерживает Mixed Content, который сплошь и рядом встречается в xhtml;
  2. NanoXML Lite, как и любой другой, не поддерживает сущность &nbsp;, что затрудняло построение системы xhtml-шаблонов (см. ниже).


Поддержку XPath пришлось написать самому (112 строчек отформатированного кода), по-моему получилось неплохо:

@Test
public void testXPathSlashInAttributeValue2() {
	XMLElement xml = XMLUtils.createElementFromString("<xml a='../../../file.txt'><value b='0' /></xml>");
	
	List<XMLElement> results = xml.find("//[@a='../../../file.txt']/value");
	
	assertEquals(1, results.size());
	assertEquals("0", results.get(0).getAttribute("b"));
}

Что касается Web Framework'ов, сначала я хотел использовать Tapestry5, потому что хорошо с ним знаком, но так как последняя версия (5.1) не поддерживается, как раз из-за ограничений XML-парсера, я решил от него отказаться. Тем кому интересно, предыдущая версия — 5.0.18 — судя по отзывам, работает.

В результате я решил вообще отказаться от Web-фреймворка и сделать все на Servlet API. Для этого пришлось реализовать пару вспомогательных классов, работающих с xml-шаблонами страниц, в основе которых лежал xhtml и измененный NanoXML Lite.

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

private void responseWithQueryForm(HttpServletResponse resp) throws Error,
		IOException {
	XMLElement indexXml = TemplateLoader.loadTemplate("index-template.htm", "UTF-8");
	XMLElement queryFormXml = TemplateLoader.loadTemplate("query-form-template.htm", "UTF-8");

	ResultsXhtmlRender.fixCssLinks(indexXml);
		
	XMLElement queryDiv = indexXml.findById("query-form");
	queryDiv.setContent(null);
	queryDiv.addChild(queryFormXml);

	resp.setContentType("text/html");
	resp.setCharacterEncoding("UTF-8");
	
	resp.getWriter().println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
	resp.getWriter().println(indexXml.toString());
}

Как все работает


Схема работы сервиса очень простая.

Введенную пользователем строку запроса, сервис по очереди передает поисковикам «КиноКопилка» и «КиноПоиск» (это еще одно ограничение GAE/Java — нельзя использовать многопоточность). Именно это объясняет, что время выполнения запроса требует 5-10 секунд. Кроме того, данные о рейтингах и некоторые другие детали, которые отображаются в результатах поиска, находятся на других страницах и не отображаются на страницах поиска кино-сервисов, что также замедляет время работы.

Для парсинга HTML-страничек GAE/Java предоставляет URLFetchService — достаточно удобное API (советую посмотреть раздел Low-Level API), построенное поверх Apache HTTP Client. Сам Apache HTTP Client на GAE/Java работать отказался.

Изначально я хотел парсить результаты моим XML-парсером, но из-за того, что «КиноКопилка» и особенно «КиноПоиск» производят non well formed xml, большинство парсинга происходит при помощи регулярных выражений. Почему большинство — потому что в КиноКопилке xml почти правильный, там есть один лишний закрывающий тег (надеюсь его подправят :), сейчас я ставлю недостающий открывающий тег ручками и пользуюсь NanoXML Lite + SimpleXPathService. (Я не просто так привел в конце ссылки на xhtml/css валидаторы, надеюсь популярные ресурсы все-таки рано или поздно будут ими пользоваться. Начинающим пора об этом задумываться уже сейчас.)

В результате получается модель объектов Movie и Rating (в предметной области всего два класса сущности), содержащая результаты парсинга.

Далее эта модель в зависимости от требуемого выходного формата рендерится либо в виде xhtml с использованием системы шаблонов, либо при помощи xml-рендера, основанного на NanoXML Lite.

Примеры выполнения поисковых запросов представлены на рисунках ниже:

image

image

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


Сервис доступен для публичного использования, включая REST API, которое вы можете свободно использовать в своих проектах, как мы это делаем на planet33, чтобы получать дополнительную информацию о фильмах.

У меня также были планы написать мобильного клиента для этого API, но я нашел другое решение, которое меня вполне устраивает, а именно использование минималистской блочной верстки xhtml + handheld CSS media type, которое позволяет пользоваться сервисом в Opera Mini на моем Sony Ericsson.

Я также выложил исходные коды проекта для ознакомления на Google Code под лицензией GPL, надеюсь они окажутся для вас полезными и вы узнаете для себя что-то новое.

Структура проекта представлена на рисунке:

image

В проекте есть несколько предупреждений (Warnings) — это дело рук Google Eclipse Plugin, он говорит о том, что я удалил неиспользуемые jar'ы из папочки WEB-INF/lib

Чтобы у вас заработал поиск по «КиноПоиску», вам нужно добавить файл Config.properties в src/dmitrygusev/ratings/utils/ со следующим содержимым:

kinopoisk-username=your-kinopoisk-username
kinopoisk-password=your-kinopoisk-password


Использованные ресурсы


  1. Ratings Service на Google Code — code.google.com/p/ratings-service
  2. Google App Engine / Gettings Started: Java — code.google.com/appengine/docs/java/gettingstarted
  3. NanoXML Lite Sources — prdownloads.sourceforge.net/nanoxml/NanoXML-2.2.1.tar.gz
  4. Online-валидация CSS/XHTML от w3c, соответственно, jigsaw.w3.org/css-validator и validator.w3.org
  5. Блочная верстка CSS — webmolot.com/css
  6. Mobile style — CSS Mobile Profile 2.0 — dev.opera.com/articles/view/mobile-style-css-mobile-profile-2-0
  7. Сервис обратной связи Реформал.ру — reformal.ru
  8. FAMFAMFAM Silk Icons (набор иконок в формате PNG, см. звездочку image в favicon сервиса) — www.famfamfam.com/lab/icons/silk
Теги:
Хабы:
Всего голосов 13: ↑12 и ↓1+11
Комментарии3

Публикации