Part I. It sucks.
Что такое WURFL? Вкратце, это 6-мегабайтный XML-файл, содержащий 130 тысяч строк. В принципе, уже этого более чем достаточно, чтобы ответить на вопрос в заголовке, потому что ни один нормальный человек в здравом уме и трезвой памяти не станет иметь дела с таким ужасным монстром (к тому же, постоянно растущим – пока, писал, вышла новая версия, которая подняла нас на новые высоты умопомрачения: теперь это 7 мегабайт и больше 145000 строк). Однако ходят слухи (непроверенные), что многие люди используют этот замечательный пример инженерной мысли в своих приложениях. Давайте же узнаем, что в этом девайсе настолько круто, что заставляет разработчиков связывать с ним судьбу своих и без того безнадежных проектов. Легче всего узнать, что об этом думает сам автор произведения. На своем сайте, в соответствующим образом названном разделе («What's cool about the WURFL?»), автор оглушает нас новостью о том, что... держитесь крепче... итак, главное преимущество вурфла перед другими решениями, это (барабанная дробь) – его малый размер! Да! Вы не ослышались! Более того, автор даже объясняет, за счет чего достигается это чудесное преимущество – вместо того, чтобы хранить всю информацию об устройстве вместе с этим самым устройством (как, по всей видимости, делают другие, неэффективные решения), эта информация вытащена, разбита на куски и беспорядочным образом разбросана по всему файлу, сопровожденная десятком тысяч ссылок, в безумной надежде, что благодаря этому для всех телефонов Nokia название производителя будет указано только один раз.Нет, я понимаю, что эта страница не обновлялась с далекого 2002 года, когда wurfl весил «всего лишь» 200 KB, а поскольку за последующие пять лет других преимуществ найти не удалось, утверждение о малом размере было оставлено с целью хоть чем-то заполнить страницу, которая теоретически должна объяснять, почему этот файл вообще имеет право на существование, даже несмотря на то, что это утверждение так очевидно противоречит той ужасной действительности, которой является нынешний размер предмета обсуждения. Проблема в том, что даже 5 лет назад это утверждение было неправдой.
Лирическое отступление. XML – все еще довольно модная штука, а 5 лет назад он вообще был на гребне популярности, поэтому его пихали туда и сюда вне зависимости от того, насколько его использование действительно оправдано там или здесь (в 95% случаев оно, конечно же, не оправдано ничем). Но я еще ни разу не слышал, чтобы кто-то аргументировал применение XML тем, что это уменьшает размер. Просто потому что с точки зрения размера это самый неэффективный формат в мире.
Проведем простой тест. Выбросим из wurfl все, что не является названием телефона, или названием производителя, или разрешением экрана. Получим файл весом в 1,2 MB. А теперь те же данные запишем в формате CSV, но имя каждого телефона запишем полностью, и разрешение укажем столько раз, сколько всего телефонов с разрешениями существует (ужасно неэффективно с точки зрения автора вурфла). Результат – 70 KB.
Итак, основное и единственное преимущество вурфла на деле оборачивается проигрышем в 17 раз. Теперь поговорим о недостатках.
Но тут самое время прервать меня возмущенными воплями. Хорошо, скажет какой-нибудь вурфлофил, рассказы о размере – это не более чем бред, но ведь есть же еще одно преимущество! Ведь вурфл содержит правдивую информацию, в отличие от неправды, которую сообщают производители в своих uaprof!
Однако это нельзя считать преимуществом перед другими решениями, потому что uaprof – это не решение. Более того, это утверждение вообще не относится к техническим вопросам, а относится к вопросам веры. В том смысле, что если кто-то действительно верит, что информации, которую дают производители о возможностях своих телефонов нельзя полностью доверять (я тоже в это верю), зато информации, которую сообщил какой-то анонимный тестер из Зимбабве, купив подержанный серый телефон на местной барахолке, можно доверять на 100%, то флаг ему в руки и бронепоезд навстречу, но технические вопросы тут ни при чем.
Возвращаясь к недостаткам, заглянем внутрь файла. Первое, что бросается в глаза – это отсутствие указания на какую-нибудь схему или DTD. Так что те наивные из нас, которые надеялись автоматически сгенерировать красивые Java-классы для сравнительно удобной работы с этим монстром (с помощью JAXB или XMLBeans или любого другого средства XML-to-Java binding), могут сразу расслабиться. Справедливости ради, на сайте можно найти какое-то DTD, но в самом файле никакой привязки к нему нет, и степень актуальности этого DTD также остается невыясненной (может быть, оно было актуальным в том же 2002 году).
Второе, что обращает на себя внимание – это куча ненужной информации. В частности, заботливо перечислены все те люди, которые поддерживают вурфл, те, кто являются его авторами, и те, кто понемногу в него контрибутят. Возможно, в мире существует один или два человека (не считая самих перечисленных), которых интересует эта информация, да и то, по всей видимости, она им нужна, чтобы разместить на своем сайте страницу с текстом типа «Наш сайт такой тормозной, потому что мы используем вурфл, если Вы хотите сказать спасибо его авторам, то вот их электронные адреса». Нет, серьезно, если вы так гордитесь своим произведением, разместите свой большой список на своем сайте вместе с контактной информацией – там он будет красиво и внушительно смотреться. Зачем пихать его внутрь файла, который содержит данные о возможностях телефонов?
Теперь попробуем задать вурфлу какой-нибудь простой вопрос. Например, такой: какое разрешение экрана у телефона LG L342i? Для начала нужно найти сам телефон. Тут есть два варианта: либо мы сначала находим все телефоны, у которых модель называется “L342i”, а потом идем по fallback-ссылкам, пока не наткнемся на название производителя. Ту ветвь поиска, у которой это название будет “LG”, объявляем правильной. Либо же мы сначала находим телефон с нужным производителем, потом проверяем все телефоны, которые ссылаются на этот: нет ли среди них телефона с нужной моделью? Если нет, проверяем все телефоны, которые ссылаются уже проверенные, и так далее, пока не найдем то, что ищем. Вероятно, приличный сервер за минуту сможем провести такой поиск, а то и секунд за 30 (напоминаю, что все это происходит путем выполнения XPath внутри 7-мегабайтного XML файла). После того, как нужный телефон найден, побегаем еще какое-то время по ссылкам на более общие устройства, пока не найдем данные о разрешении экрана.
Мне кажется, что я знаю систему, которая построена на очень похожих принципах. Эта система называется Cocoon, и, будучи основанной на XML/XSLT, она как нельзя лучше подходит для WURFL. Вместе они способны шокировать мир, подняв производительность и удобство поддержки на невиданные глубины.
Part II. It still sucks.
Я уже слышу недовольные возгласы. Мол, совершенно необязательно использовать вурфл напрямую; ведь для него же существует простое и удобное Java API!
Первая проблема, которая возникает с этим API, заключается в том, что то, что о нем сказано на сайте, не имеет ничего общего с реальным положением вещей. В частности, увидев сообщение о существовании волшебного метода ObjectsManager.wurflWebInit(), который сам ходит в интернет, забирает оттуда wurfl и парсит его, мне немедленно захотелось им воспользоваться и посмотреть, что из этого выйдет. Однако мне не удалось этого сделать, потому что в реальности такого метода не существует. Это вполне понятно, страница последний раз менялась давно, API не до такой степени давно, метод убрали, а на приведение документации в согласованное состояние, в лучших традициях OSS, забили. Поэтому в дальнейшем я ориентировался только на javadocs. Кстати, несмотря на разбросанные по сайту намеки на то, что WURFL имеет какое-то отношение к Open Source, найти исходные тексты этого API лично мне не удалось. А жаль, потому что есть сильное подозрение, что там есть над чем посмеяться. Впрочем, даже java-доки предоставляют достаточно почвы для этого.
Самый смешной класс - net.sourceforge.wurfl.wurflapi.ListManager. Прежде всего, что такого плохого в идее возвращать интерфейсы, как это делают все приличные люди и как рекомендуется в абсолютно всех руководствах? Нет, каждый метод этого класса возвращает конкретную реализацию, то есть ArrayList вместо List, HashMap вместо Map и т. д. Далее, до сих пор я думал, что метод с названием типа getBlaBlaSet с большой вероятностью должен возвращать... Set. А метод с названием getBlaBlaList наверняка возвращает List. Все не так в этом удивительном классе. Здесь метод getCapabilitySet возвращает ArrayList, а метод getDeviceElementsList возвращает HashMap. Но и это еще не все. Автор, по всей видимости, понятия не имеет о принципах генерации javadoc’ов. В частности, он явно не в курсе, что кратким описанием метода является первое предложение, его документирующее, а предложением считается группа слов до первой точки с пробелом. В результате в Method summary можно увидеть удивительные вещи вроде «Return HashMap of Arraylists of Capabilities (i.e.». Ну и, наконец, комментарий «Return ArrayList of device IDs to WurflDevices» заставляет меня признать гениальность автора, сумевшего углядеть семантику Map в ArrayList.
Но стоить признать, что автор не совсем безнадежен. Он явно уже в 2004 году осознал, насколько болезненной процедурой является парсинг его собственного XML-файла. Эту проблему он решил простым и элегантным способом. Итак, чтобы избавить разработчиков от мучений, связанных с необходимостью парсить мегатонный XML-файл при обновлении этого файла, мы... лишим их такой возможности! Инициализацию можно провести только один раз, и после этого никакие обновления невозможны! А если кто-то все же хочет использовать более новую версию файла, ему нужно всего лишь перезапустить свое приложение – и тогда у него появится шанс, что новый файл будет замечен и принят во внимание (но если библиотека лежит в общей области видимости, то и этого шанса у него нет, и единственным выходом будет перезапуск всего сервера приложений). Такой метод решения проблем уже получил свое название – «Лечение язвы желудка с помощью геморроя».
Никто не любит, когда его считают идиотом. И я в этом смысле не исключение. Но автор очевидным образом считает идиотами всех, кто решит использовать его замечательное API. Я имею ввиду, что я в курсе, что такое синглтон, и если мне будет нужно, я смогу его реализовать. Что мне нужно, так это объект, который я мог бы проинициализировать в нужное мне время, а потом обновлять по мере необходимости. А уж создать его, и поместить в нужную область (в application scope, разумеется, а не в статическое поле класса) я и сам как-нибудь смогу. Но вместо того, чтобы дать мне такую возможность, меня осчастливили специальным сервлетом, единственное назначение которого – инициализация вурфла при старте приложения (то, что все остальные люди делают в EventListener’ах), и ради которого в библиотеку вводится зависимость от Servlet API!
Стоит согласиться с автором, только идиоты могут рискнуть использовать такое API.
Но вурфлофилы не сдаются. Хорошо, говорят они, Java API оказалось тупым отстоем, но ведь его необязательно использовать! У нас есть WALL! Теперь нам вообще не нужно ничего знать ни о телефонах, ни о языках разметки, ни об XML, ни о Java! Достаточно просто написать , и тут же наступит счастье!
Ну, что касается WALL, достаточно сказать, что эта прекрасная библиотека JSP-тэгов построена на том самом API, и поэтому наследует все его недостатки, но добавляет к ним свои собственные. Исходного кода так же нет, но куски его автор любезно опубликовал в документации. Теперь мы знаем, что требуется 21 строчка кода только для того, чтобы определить, писать ли нам “ <br/>”, или все же “ <br>”, и это только в случае XHTML и CHTML, а ведь есть же еще и WML, и HTML. Жаль, что я еще не видел ни одного даже самого тупого телефона, который бы каким-то неправильным образом истолковывал тэг <br/> , иначе я бы наверняка проникся полезностью и уникальностью этой фичи. К сожалению, удивительные возможности, которые открывает нам WALL, имеют свою цену. Автор предлагает нам забыть о том, чтобы страницы нашего сайта выглядели не слишком убого, и радоваться уже тому факту, что они хоть как-то отображаются на большинстве устройств.
Особый интерес у меня вызвал раздел сайта, озаглавленный «Does the WURFL API and WALL Scale». Содержимое, впрочем, уже довольно предсказуемо разочаровало. Выяснилось, что автор не понимает значения слова «масштабируемость». Иначе он не приводил бы в доказательство масштабируемости тот факт, что какое-то приложение, использующее WALL, смогло потянуть 50000 запросов в день. Более того, эта смешная цифра говорит о том, что и о производительности он не слишком наслышан. Потому что люди, говорящие о высокой производительности, оперируют цифрами, выражающимися в миллионах запросов в день. А 50 тысяч... столько может без особого напряжения выдержать любой из наших последних порталов. Только не в день, а в час. А если с некоторым напряжением – то и за полчаса.
Part III. It will suck.
Ходят слухи, что автор осознал, насколько бесполезными оказались всего его продукты в реальном мире. Однако, вместо того чтобы убить себя об стену Колизея, или, на худой конец, объявить миру о том, что проект оказался тупиковой ветвью эволюции и закрыть его (как это делают некоторые более сознательные представители мира OSS, например, так сделали разработчики Avalon, что, впрочем, не мешает кокуну и дальше основываться на библиотеке, которую ее собственные создатели признали недостойной дальнейшей жизни), автор занят тем, что пишет... новое API для старого файла!
Мир был бы прекрасным местом, если бы люди учились хотя бы на своих собственных ошибках, не говоря уже о чужих. Какое API для WURFL не напиши, оно в любом случае будет бесполезным, потому что оно не сможет избежать следующих проблем:
1. Единственный способ работать с WURFL – это держать все его данные в памяти, потому что нетривиальный поиск в 7-мегабайтном XML-файле при каждом запросе автоматически делает производительность отрицательной величиной. При этом большая часть этих данных абсолютно бесполезна, как, например, данные о не-i-mode телефонах на i-mode сайте, или как данные о поддержке рингтонов на сайте, который предлагает скачать картинки. Но вытащить из вурфла часть данных практически невозможно, благодаря его прогрессивной и эффективной структуре.
2. Инициализация данных занимает чертову кучу времени и памяти, и в дальнейшем будет занимать все больше и больше.
3. Обновление (если нам дадут возможность его делать) занимает столько же ресурсов.
4. И самое главное – данные должны лежать в базе данных, а не в каком-то дурацком файле!
Part IV. How it could be.
Сказав столько об отстойности вурфла, нельзя не сказать о том, как можно было бы реализовать хранение данных о различных телефонах, если бы автора хоть немного волновало улучшение качества жизни конечных пользователей. Итак, чего хочет пользователь (то есть я)? Не так уж и много. Мне нужно:
1. Хранить нужные данные в своей базе, а ненужные данные в ней не хранить.
2. Уметь инициализировать эту базу при самом первом запуске приложения, желательно удобным и стандартным способом.
3. Уметь обновлять эти данные по мере необходимости, желательно таким же удобным способом.
То есть фактически, мне нужен сервис, который даст мне доступ к нужной информации. Архитектура такого сервиса на первый взгляд выглядит достаточно очевидной. На второй взгляд, правда, тоже. Мы храним данные о телефонах (и не только о телефонах) в базе данных, и предоставляем к этой базе различные интерфейсы: HTTP, SOAP, XML-RPC, RMI, любой другой более-менее распространенный. А эти интерфейсы содержат методы, которые дают пользователям возможность получить нужные им данные и сгенерировать на их основе удобное им представление этих данных. А уж представление может быть любым – они могут положить эти данные в базу, или в файл, или оставить в памяти... особые извращенцы смогут даже, используя эти интерфейсы, сгенерировать себе WURFL.
Мало? Для тех, кто не хочет расставаться с файлами, можно на регулярной основе генерировать из базы файлы в нужном формате (форматах), и предоставлять к ним доступ через WebDAV. Или хранить эти файлы в Subversion. Или где угодно еще.
Что касается конкретного содержимого базы, то тут простор для фантазии ничем не ограничен. Например, можно сохранять дату последней модификации сведений о телефоне. Или хранить информацию о том, какой оператор какие телефоны поддерживает. Тогда пользователи смогут делать запросы типа «дайте мне всю информацию об i-mode девайсах, которые поддерживает Bouygues, и которая была изменена/добавлена после 1 ноября». По-моему, это круто. Главное, на начальном этапе спроектировать базу так, чтобы потом легко можно было добавлять что-то новое и при этом поддерживать обратную совместимость.
Возможно, что такие решения уже существуют. Возможно, это одно их тех коммерческих решений, о которых нехотя упоминает автор вурфла.
Что касается генерации страниц на конкретном сайте, то здесь можно рассмотреть, например, Mobile JSF. Это решение использует, в частности, концепцию подключаемого менеджера устройств, то есть разработчик сам решает, откуда ему брать информацию о телефонах, из базы, из вурфла или еще откуда-то – достаточно реализовать один интерфейс. Помимо этого и других бонусов этого решения, мы получаем и все бонусы JSF, то есть независимость от технологии представления (если мне не нравится JSP, я могу использовать Facelets), и бесплатный контроллер. Все недостатки JSF, правда, мы получаем тоже , но с ними, как показала практика, все же можно иметь дело.
Но главное, что вопрос об использовании информации о телефонах на конкретном сайте не имеет большого отношения к вопросу о хранении всей информации обо всех телефонах в мире и доступу к этой информации.