Опыт разработки GUI на JSF

    Разработкой браузерных GUI под явой я занимаюсь уже около 12 лет, среди прочего имею опыт использования сервлетов, JSP, XML/XSLT, Struts, Tapestry (3,4,5), Wicket, Spring MVC и GWT.

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

    Время шло, JSF не собирался умирать, а появился уже в версии 2.1. И я решил попробовать использовать его для одного из своих экспериментальных проектов, на которые я трачу примерно 3 месяца каждый год — для расширения кругозора и отвлечения от скучного консалтинга (которым зарабатываю детишкам на молочишко в остальные 9 месяцев).



    На голых HTML-формах делать GUI в 21 веке скажем прямо, неприлично. Поэтому хотелось, разумеется, чтобы был AJAX и всякие такие аппетитно-сексуальные виджеты, что называется прямо из коробки.

    В качестве кандидатов на рассмотрение были выбраны, по итогам чтения интернетов, следующие три — RichFaces, PrimeFaces и ICEFaces. Использовались последние доступные (включая беты, RC и т.п.) версии фреймворков.

    Как это ни прискорбно, первые два фреймворка срезались на первом же (слава Джа! что первом), наитупейшем тесте.

    То, что мне предстояло построить — насыщенный формами бэкенд приложения. Следовательно, не обойтись без табов.
    К сожалению, RichFaces и PrimeFaces не понимают, что при рендерении набора табов вида:

    <panelTabSet>

    <panelTab label="First tab">
    ...
    </panelTab>

    <panelTab label="Second tab">
    ...
    </panelTab>
    ...

    </panelTabSet>


    не надо рендрить то, что в «Second tab», если выбранный таб — «First tab». RichFaces и PrimeFaces всегда создают дерево объектов, включая всё содержимое TabSet, т.е. все невыбранные табы в том числе. Это было абсолютно неприемлемо: данных в каждом табе было много, в том числе того, что достаётся из базы данных.

    Кроме этой прискорбной ущербности, из RichFaces и PrimeFaces там и тут просачивалась необходимость использования JavaScript, которой мне хотелось как раз избежать.

    ICEFaces, на удивление, не только вменяемо относился к рендерению табов, но ещё и содержал такие компоненты, которые просто работали, безо всякого JavaScript.

    В результате за примерно 120 часов работы удалось приделать к существующему ядру достаточно развесистый GUI (12 больших и сложных форм), программируя только в виде Facelets-разметки (местами с CSS), и в Java, т.е. не вдаваясь ни в какой Javascript.

    Вот так это всё примерно выглядит:

    Скин — один из дефолтных, называется 'rime', так что эстеты, возможно, будут недовольны.

    Важный итог: теперь я знаю, для чего можно использовать JSF (то есть не вообще JSF, а конкретно ICEFaces) — для backend-интерфейсов, не очень сильно нагруженных (порядка сотен пользователей), и при незначительном расстоянии между браузером и сервером.

    Overhead на излишнюю коммуникацию с сервером компенсируется возможностью быстрого программирования на высоком уровне. И, в общем-то, всё работает. Работает даже server push (обновление экранов по событию на сервере).

    Использовались следующие технологии:
    — Maven, ну куда без него
    — ICEFaces, см. выше.
    — MyFaces вместо стандартной Mojarra — по причине лучшей диагностики ошибок.
    — Spring Core
    — Spring Security
    — Selenium
    — Jetty

    Отдельное хорошее слово хочу сказать про JRebel — инструмент для автоматизированного деплоймента изменённого кода без остановки VM. Я имею их лицензию для open-source разработки, но вообще говоря JRebel стоит тех скромных денег, которые товарищи из Эстонии за него просят. Отличнейший продукт, работает как заявлено и экономит время.

    Вся разработка велась в IntelliJ IDEA. Но, полагаю, в Eclipse/Netbeans всё должно работать точно так же.

    Ах да, ради чего я это всё начал писать…

    Собственно, из моего проекта была дистиллирована его суть и сохранена на гитхабе в виде
    сего скромного артефакта.
    Собрался было из него сделать Maven archetype, но стало лень.

    В проекте присутствует всё заявленное выше, и работает базовая login/logout форма, для которой написан также Selenium-тест. Детали в гитхабовом README.

    Надеюсь, кому-нибудь это поможет сэкономить немного времени на старте важного проекта.
    У меня выбор и подгонка компонентов заняла почти две недели.
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 48
      +1
      Спасибо за обзор.
      Я вот ранее использовал Wicket и GWT для создания интранет приложений. Впечатление сложилось такое же — замечательно для не очень сильно нагруженных проектов. Можете посоветовать что-то для более нагруженных?
      Пока что в таких случаях использую JSP, но как-то уже надоело.
        +9
        мой выбор для нагруженных приложений — это как раз GWT
        в отличие от JSF-фреймворков, в GWT вы сами определяете, когда клиенту ходить на сервер и какие данные с него получать

        ну, есть ещё самый крайний случай — с наибольшими возможностями по оптимизации, но при этом наиболее трудоёмкий в разработке и отладке
        это клиент на JQuery и HTTP/JSON сервер, на чём удобнее, скажем на каком-нибудь RESTeasy
          +2
          gwt штука хорошая, главное вовремя сказать нет, когда заказчик захочет превратить интернет приложение в интернет. Подтачивать gwt под браузеры это маленький ад, особенно при использовании сторонних компонентов.
        +1
        Эх, люблю я Джаву, отличный топик.
          +5
          >К сожалению, RichFaces и PrimeFaces не понимают, что при рендерении набора табов
          не надо рендрить то, что в «Second tab», если выбранный таб — «First tab». RichFaces и PrimeFaces всегда создают дерево объектов, включая всё содержимое TabSet, т.е. все невыбранные табы в том числе.

          У RichFaces есть атрибут для настройки этого. Разве он не работает?
          <rich:tabPanel switchType="ajax">
          
          </rich:tabPanel>
            0
            Как я понимаю, тут требуется, чтобы DOM-дерево не строилось, но все данные выгрузились скопом.
            Просто очевидно, что просто загрузка данных тормозит работу не сильно, а вот работа со страницей с монструозным домом — будет медленной.
              +2
              атрибут есть, да
              это управление тем, как происходит переключение таба
              но всё равно — рендрятся все элементы во всех табах

              вот одно из описаний проблемы
              на момент 4.1.0.Milestone3 воз был всё ещё там, я не смотрел на Milestone 4 и CR1
                +1
                Если Вы про рендеринг на события ajax, их надо разделять регионами.
              +3
              Спасибо, хороший обзор. Хотел уточнить вещь: бэкенд интерфейс — вы что под этим подразумеваете? Мониторинг утилизации ресурсов, судя по скриншотам?

              От сбя дбавлю про jsf. Не знаю что там поменялось в версии 2, но версия 1.2 имела веселые вещи, типа различия имплементаций саном и myfaces. Вроде как richfaces работали только с одной. Так же там постоянно протекали абстракции динамических компонентов, что заставляло подписывать свой javascript с бошими трудностями по его дебагу.

              Другой забавной штукой было хранени состояния view. Либо ты его хранишь на сервере, где он хранится в сессии, либо на клиенте. Состояние — это сериализоанный граф jsf компонентов. Если использовать всякие няшки от jsf по и18ции, а также например больше одной формы на странице, то просто можно получить страницу весом мегабайт 5 чистого хтмла из-за сериализованного там viewstateа. При использовании по-моему richfaces, компонент который хранит состояние на клиенте переопределен и тупо вовращает null при попытке его получить (хвала opensource).

              Вообщем, можно сказать, чо jsf я готовить не умею, но впечатление он ( в купе с улучшайзерами ) произвел очень протекающей абстракции.
                +2
                в данном случае это был бэкенд к digital asset management, но в принципе может быть какой угодно интранетный интерфейс — екоммерц, CMS, CRM, ERP и т.п.

                на клиенте стейт действительно можно хранить, но в данном случае я этого не делал
                вообще в глубины реализации я не спускался, но содержимое AJAX-запросов, которые ICEFaces гоняет туда сюда — это тоже сериализованный стейт компонентов
                но его там не мегабайты

                собственно на мой взгляд, достоинство JSF 2.1 и ICEFaces в частности именно в том, что удалось добиться того, что было обещано изначально — сложность реализации и абстракции прикрыты не уродливо выглядящими и не сложными в освоении фасадами
                чтобы можно было заниматься бизнес-программированием пользовательского интерфейса на уровне код+маркап… короче, как во Flex, только сразу с привязкой к объектам на сервере
                +2
                У меня вопрос про JRebel. Вы ведь пишите, что разрабатываете на IntelliJ IDEA, но она позволяет своими средствами редеплоить приложение без остановки сервера. У JRebel есть какие-то плюсы перед штатными средствами IDEA? Почему вы выбрали именно его?
                  0
                  А можно поподробнее про штатные средства? Как это работает? И что для этого нужно — собирать проект идеей?
                    0
                    Да, чтобы это работало, нужно собирать проекты идеей. У нее есть такое понятие, как артефакты. Это то, как в конце будет выглядеть проект. Можно выбрать war-архив, тогда проект каждый раз при деплое будет собираться в war и он будет каждый раз распаковываться на сервере. А можно выбрать war-exploded. Тогда IDEA создаст папку в вашем проекте, куда будет скидывать скомпилированный проект, и будет перенаправлять сервер приложений использовать эту папку(как бы подменяя ею war). И в этом случае можно будет не перезапускать каждый раз сервер при изменении кода(IDEA использует Hot Swap для этого).
                    Правда есть и ньаюнсы. Такой вариант подходит не для всего. Например, изменение аннотаций у меня не подхватываются, приходится перезапускать каждый раз заново. Это наверное из за специфики, в которой я особо глубоко не копал. Поэтому и спрашиваю, может быть JRebel более продвинутый в этом плане инструмент.
                      +3
                      А. ну так это проблемы HotSwap — изменения подхватываются внутри метода only. Дело в том, что JRebel работает по другому — он прямо в контейнер засовывает новый байткод класса, а поэтому может работать с чем угодно — можно добавлять методы и поля классов, менять методы и прочее. Не работает только смена super-класса. Алсо, JRebel в последнее время научился вроде как подхватывать и аннтационные изменения, т.е. работать с CDI, но я не проверял. Алсо, JRebel требует от сервера больший размер PermGen. Для работы JRebel в идее нужен плагин(но это только чтобы дебаг работал по измененным классам).

                      И для идеи не обязательно делать сборку самостоятельно. Просто в ней надо сказать, чтобы она классы компилировала туда же, куда собирает ваш сборщик — ant, maven и так далее.
                        0
                        Алсо, JRebel в последнее время научился вроде как подхватывать и аннтационные изменения


                        Да вобщем то не в последнее время, а давольно давно :)
                        Подхватить аннотацию — это не довольно небольшая задача. Большая задача — в зависимости от изменения аннотации правильно подкрутить фреймворк, для которого данная аннотация используется.

                        Плагин, в IntelliJ, как и в других IDE нужен по существу для автоматизации — просто так удобней инсталлировать и запускать. Ну и да — дебаггер тоже подкручивается. Но по сути можно и без плагина (кроме дебаггера) всё настроить.
                        0
                        >
                        И в этом случае можно будет не перезапускать каждый раз сервер при изменении кода(IDEA использует Hot Swap для этого).


                        eclipse умеет использовать HotSwap и для приложений задеплоенных как WAR, собственно HotSwap и есть средство загрузки перекомпилированного кода в JVM (Class File Replacement), вне зависимости от того как приложение было запущено

                        другое дело, что HotSwap действительно сильно органичен по тому какие изменения допустимы
                      0
                      JRebel позволяет обойтись без редеплоя. Этим и отличается.
                        +2
                        По ссылке как раз сравнение с Hot Swap.
                          0
                          Да, да, увидел. Спасибо за информацию.
                          0
                          Я неправильно выразился. IDEA тоже не редеплоит каждый раз приложение на сервер. Я в значении «редеплоить» имел ввиду отражение изменений в коде на сервере.
                            +2
                            да, именно
                            если меняется логика на стороне сервера, в IDEA достаточно нажать Ctrl F9, и JRebel перегружает изменённые классы
                            при этом приложение не останавливается, и HTTP сессия остаётся, т.е. можно достичь фактической скорости разработки как в PHP или Django
                              0
                              Подскажите, что нужно использовать для разработки GWT в Idea, дабы серверные изменения подхватывались на лету?
                              Дело в том, что для модуля gwt нельзя указать использование exploded. Прикручивать JRebel?
                                0
                                Спасибо за подсказку, но Update Project при дебаге (ctrl+f10) действительно подхватил изменения! Я счастлив!
                                Правильно ли я понял, что JRebel дает возможность Idea подхватывать значительные изменения кода — добавление методов, полей и т.д.?
                            0
                            У PrimeFaces в последних версиях по крайней мере в p:dialog есть атрибут dynamic. Не то?
                              +1
                              Важный итог: теперь я знаю, для чего можно использовать JSF (то есть не вообще JSF, а конкретно ICEFaces) — для backend-интерфейсов, не очень сильно нагруженных (порядка сотен пользователей)…

                              Из статьи совершенно не понятно откуда взялся такой вывод. Почему «сотен пользователей» а не десятков или тысяч?
                              Хотелось бы видеть хотя бы минимальные замеры или сравнения производительности
                                +1
                                вывод делается просто из архитектуры

                                любые динамические изменения страницы (вплоть до «открыть/закрыть диалог») требуют обмена с сервером или же придётся спускаться на уровень JS, существенно проиграв в скорости разработки и сложности получившегося кода

                                нет контроля над обменом с сервером, вообще нет программирования «клиентского приложения» (JS, исполняемого в браузере) как такового
                                поэтому при прочих равных, я бы не стал рекомендовать JSF как технологию для е-коммерс фронтенда

                                если хочется оптимизировать, то, как мне показалось, PrimeFaces даёт наибольшую открытость в смысле встраивания JS/JQuery логики
                                но создавать такой странный гибрид мне в данном случае не захотелось
                                  +1
                                  Поддерживаю, что это прослеживается из архитектуры.

                                  Вообще, ИМО, такие библиотеки и надстройки (это и JSF, Wicket, Vaadin, GWT ) позволяют сделать где-то 50% функционала любой морды просто, 35% делается тяжело или очень тяжело, а 15 или 10 процентов вообще сделать не возможно, поэтому стоит прежде чем комитатся на какие-то дедлайны с этими технологиями, стоит понимать их возможности и предстоящую работу.
                                    0
                                    В таком случае на чем писать остается? :-)
                                      0
                                      Не хочу показаться снобом, но Frontend developer'а не должно пугать написания JS'а. :) Не говорю, что каждый раз с нуля, фреймворки никто не отменял.
                                        0
                                        Я недопонял сначала, что идет речь о написании 90% на фреймворке, и 10% на JSe. Я думал, речь про то, что эти 10% делают фреймворк полностью непригодным для проекта. Поэтому и спросил — что остается.
                                          +1
                                          Нет, я как раз говорил о фреймворках, которые я перечислил, и о 10% задач, которые делают их непригодными.

                                          Потому что подход «вам не надо думать о JS, пишите на уютной Java, мы все сделаем за вас» конкретно протекает, и делает он это в самый неподходящий момент. Поэтому писать что-то долгосрочное и agile — может быть рискованным.

                                          Если это какая-то фиксированная CRUD морда, то ок — быстрее пишется именно так. Если ничего до конца не известно, берем plain HTML и навешиваем на него свисто-перделки на JS подключая JS фреймворки, код которых доступен и читаем, а также расширяем.
                                      +1
                                      мм, я прошу прощения, но GWT — лишний в этом ряду

                                      программирование на GWT ничем в принципе не отличается от программирования на HTML+JQuery — клиентское приложение у вас получается такой толщины, какой вы захотите
                                      (при этм сервером теоретически может быть что угодно… хоть PHP, который JSON умеет отдавать, хотя родная GWT-сериализация удобнее конечно для Java)

                                      все остальные технологии исполняют GUI-код в основном на сервере и с каким попало сервером работать не могут
                                        +1
                                        Проценты того, что легко или сложно сделать, точно так же применимы для GWT
                                          0
                                          Может быть, но я не вижу как может быть кодогенерация (не трансляция) быть сколь угодно оптимальной и контролируемой.
                                            0
                                            высоконагруженные приложения нагружают сервер, а не клиента
                                            используя GWT, можно оптимизировать паттерны передачи данных так, чтобы минимизировать нагрузку на сервер
                                            тем же самым придётся заниматься, если вы пишете клиента на чистом JS или, скажем, Flex

                                            также в пользу компилятора GWT могу сказать, что мне известен случай, когда им транслировали iText, чтобы в результате иметь генерацию PDF в браузере… вроде бы даже работало
                                        0
                                        вообще нет программирования «клиентского приложения» (JS, исполняемого в браузере) как такового

                                        есть
                                          0
                                          JavascriptContext — это средство для интеграции, а не разработки

                                          например в данном проекте с его помощью был подключен CodeMirror
                                          иначе бы пришлось как-то грязно хакать в обход всей сложной машинерии ICEFaces, с риском потери совместимости со следующим ихим релизом
                                            0
                                            для интеграции чего? что мешает добавить вызов JavascriptContext.addJavascriptCall в произвольный листенер? не понимаю, где «нет программирования JS как такового»
                                              0
                                              ничего не мешает

                                              я понял, вам не понравилось моё выражение «нет программирования JS как такового»

                                              наверное, я должен был сказать иначе
                                              к примеру:
                                              «программирование JS как таковое необходимо лишь для отдельных специфических случаев, например для интеграции с JS-библиотеками»
                                      0
                                      Немного оффтоп: как вам Wicket vs Tapestry5?

                                      Я когда-то их сравнивал, но с тех пор много времени прошло, интересно узнать ваше мнение.
                                        0
                                        c точки зрения пользователя API какой-то фундаментальной разницы обнаружено не было
                                        и там и там markup + бины на сервере, т.к. ajax не использовался — про него ничего сказать не могу
                                        Wicket субъективно показался удобнее, с более «человеческим лицом»
                                        0
                                        … просачивалась необходимость использования JavaScript, которой мне хотелось как раз избежать.
                                        Я вот никак не пойму. Как у вас получается программировать под web и избегать javascript? И зачем его избегать? Он чем-то так ужасен?
                                          0
                                          избегать его хочется по причине того, что даже будучи завёрнут в нечто кошерное типа JQuery, он всё равно хрупок, сложен и проблематичен в отладке
                                          JS — это ассемблер веб-разработки, если хочется максимального качества и производительности — придётся спускаться до этого уровня
                                          если же важна скорость разработки, то чем меньше JS (в открытом виде) тем лучше
                                          как-то так
                                            0
                                            Спасибо. Всегда хотел программировать на ассемблере. Как-то не сложилось. Теперь вот хоть для веба сложилось ) А вообще, как подумать сколько абстракций и слоев между веб приложением и машинным кодом, то плохо становиться
                                              0
                                              а вы подумайте, сколько слоёв и абстракций между процессом фотосинтеза и салатом у вас на столе
                                              это вполне естественно — чем больше у устройства функций и чем человеку приятнее с ним работать, тем сложнее становится устройство
                                              главное — чтобы всё работало в целом надёжно

                                              а вообще всё зависит от решаемой задачи

                                              цель программирования в бизнес-контексте — это добиваться удовлетворительного результата за вменяемое время
                                              я бы рекомендовал использовать JSF только если цель — сделать _быстро_, за счёт возможной потери в качестве user experience, ну да ладно, бизнес-гуй не обязан быть идеальным

                                              если надо вылизывать производительность/UX на клиенте и по возможности снижать нагрузку на сервер — надо использовать всё что ниже, вплоть до голого JS

                                          0
                                          Кстати, у primefaces есть опция для динамической подгрузки контента табов
                                          www.primefaces.org/showcase-labs/ui/tabviewDynamic.jsf но это в новых 3.0.x версиях.
                                            0
                                            Из коробки почему-то артефакт заработал странно. При первом нажатии на кнопку логина ничего не происходит, чтобы войти нужно повторное нажатие. Проовал в IE и Опере.
                                              0
                                              Нельзя ли было бы обойтись без Spring'а?

                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                            Самое читаемое