Разработка веб-приложений на встраиваемом портале +3


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

На рынке этих систем существуют коммерческие продукты от таких известных компаний как Oracle, SAP, IBM (теперь HCL Technologies Ltd), Red Hat JBoss, и кроме того есть также портал Liferay имеющий приличную опенсорс версию, а также образовательные и другие тематически-ориентированные портальные системы, есть и не вполне порталы, но информационные системы, поддерживающие технологии порталов такие как DMS Alfresco. Поддерживающие означает даже, что в теории приложения разработанные для одной системы можно устанавливать в другую, но на деле это было не совсем так. Производители добавляли свои возможности несовместимые с другими, что оставляло эту не имеющую аналогов фичу в нереализованных.

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


С точки зрения пользователей порталы обычно позволяют легко управлять большим количеством сайтов их ресурсов и приложений не выходя из “админки”, гибко настраивать отображение материалов на страницах без кодирования. В то же время для разработчиков портальные системы остаются платформой с приличным стеком технологий.

Как и другие java технологии портальные приложения хорошо стандартизированы. Есть стандарты Portlets 1.0 по признаку поддержки которого можно причислить веб системы к джава порталам, также многие развитые порталы поддерживают Portlets 2.0, и пока еще не многие относительно новый стандарт Portlets 3.0. 

Третья версия стандарта на приложения портлетов привносит много долгожданных фич для приложений, которые позволяют создавать веб-приложения для порталов так-же просто и легко как не знаю прямо уже что;). Возможно стартеры проектов npm или spring-boot тут будут подходящей аналогией. При этом сама по себе концепция портлетов т.е. виджетов размещаемых на одной странице всегда была несколько сложнее монолитных или SPA приложений, поэтому и разработка портлетов выходящих за рамки “одного окна” была сложнее. И вот третья версия многие эти сложности устраняет, позволяя модульно и переиспользуемо управлять фронтенд ресурсами приложения. Говоря конкретнее: теперь можно поделить один метод отдающий JSON или файлы между портлетами, можно контролировать загрузку разных джаваскриптов с помощью механизмов портала таким образом, что они не будут загружены дважды, объявлять портлеты и их методы теперь можно декораторами без всех этих сложных для новичка xml-ей, но совместимость с этими конфигурациями продолжает работать. 

Поддержка стандартов разработки порталов позволяет устанавливать приложения написанные под один портал на другой, а также запускать на сервере с порталом всевозможные веб-приложения на джаве позволяя выстраивать всевозможные  архитектуры включая сервисно-клиентные. Т.е. в портале заработают и классические сервлеты и различные java фреймворки поддерживающие сервлетные контейнеры, такие как Spring, Struts, Faces, Jersey и многие прочие. А на некоторых порталах можно даже использовать другие языки программирования навроде PHP или Ruby.

Также порталы часто поддерживают технологию WSRP позволяющую приложения управляемые одним порталом показывать на любом другом настроенным на поддержку этой технологии. 

Некоторые портальные системы кажутся достаточно тяжелыми по требовательности к ресурсам. Разворачивание может потребовать от 4-х и более гигабайт оперативной памяти. Их конфигурации рассчитаны на кластеризацию и разрабатывать для них без специальных инструментов от вендора выходит не очень удобно.

Но если придерживаться стандартов портальной и Java разработки, то можно запускать их на любом поддерживаемом портале и для разработки в этом случае может сгодиться достаточно простой и “легкий” портал.

Таким порталом является Apache Pluto, который выполняет роль основы для многих портальных систем и референсной реализации, т.е. образцом реализации стандартов и технологий для производителей.

Чтобы получить самые актуальные версии соберем все из исходных кодов.

Далее мы будем использовать консольные команды которые работают в Unix-like операционных системах или в Windows если включить при установке дистрибутива git поддержку shell утилит. Убедившись что git установлен и откликается в cmd.exe или другом вашем терминале склонируем проект с гитхаба

git clone https://github.com/apache/portals-pluto

Собираем дистрибутив. Для того, чтобы все наверняка собралось потребуется джава какой-нибудь стабильной версии, например 8ой и установленный сборщик ant. Проще конечно это все будет поставить пакетной системой дистрибутива вашей ОС. Для запуска сборки переходим в каталог с проектом и выполняем команду

mvn package

для сборки и для упаковки дистрибутива

ant -f dist-build.xml -DpackageOnly=true

также лучше добавить флаг -DincludeDemos=true для включения примеров в бандл, но у меня также потребовалось подкорректировать билд-файл демо подпроектов чтобы имя дистрибутива не содержало версий, в demo/pom.xml в секцию build я добавил настройку

<finalName>${project.name}</finalName>

на Windows мне также пришлось и добавить пропуск плагина в pom.xml,т.к. он где-то заваливался

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-gpg-plugin</artifactId>
  <version>1.6</version>
  <configuration>
     <skip>true</skip>
  </configuration>
</plugin>

После успешного завершения сборки в подкаталоге проекта target/dist появится файл дистрибутива, примерно такого вида. Это самодостаточный дистрибутив портала установленного в контейнер, т.е. сервер приложений готовый к развертыванию.

pluto-3.1.1-SNAPSHOT-bundle.tar.bz2

Теперь у нас есть собственный дистрибутив портала.

Мы его распакуем куда удобно, например рядом на с:\projects

cd c:\projects
tar xvjf c:\portals-pluto\pluto-3.1.1-SNAPSHOT-bundle.tar.bz2

Перейдем в распакованный каталог и запустим на наш сервер

cd pluto-3.1.1-SNAPSHOT

bin\startup.bat

или startup.sh если вы на юниксе.

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

На всякий случай лучше посматривать за логами, это можно делать вот такой командой:

tail logs\catalina-дата.out -f

в Windows они будут отображаться прямо во вновь открывшемся окне.

появление сообщения вида

org.apache.catalina.startup.Catalina.start Server startup

говорит нам о том, что сервер готов служить, после чего мы можем открыть в браузере адрес

http://localhost:8080/pluto

Логин - pluto и такой же пароль.

Слева будут ссылки на страницы с примерами различных приложений.

Чтобы быть готовым это увидеть, надо понимать, что Apache Pluto это демонстрационная, можно сказать, академическая разработка, он не претендует на продуктовое совершенство и привлекательность для пользователей, которое вы можете найти в других системах таких как Liferay или WebSphere/HCL DXP. 

Страницы можно конфигурировать через панель управления в разделе Pluto Admin, добавляя и убирая страницы и размещая на них портлеты.

http://localhost:8080/pluto/portal/Pluto%20Admin

Код этих примеров можно посмотреть в каталоге demo, импортируя их в среду разработки, собирая и деплоя на портал копируя .war файлы в подкаталог webapps/

Теперь создадим новое приложение из архетипа. Для этого понадобиться установленный maven, может быть даже довольно актуальной версии. Для windows установка примерно сводится к распаковке дистрибутива с сайта и прописывании переменной M2_HOME в окружения и добавления путя M2_HOME\bin в Path.

cd projects
mvn archetype:generate -DarchetypeGroupId=org.apache.portals.pluto.archetype -DarchetypeArtifactId=mvcbean-jsp-portlet-archetype -DarchetypeVersion=3.1.0 -DgroupId=com.mycompany -DartifactId=hello-portlet

Если залипло или что-то спрашивает нажимайте Enter.

Лучше в pom.xml сразу добавить имя выходного файла без версии.

<finalName>${project.name}</finalName>

Соберем проект и задеплоим.

Данный архетип поддерживает сборку под разные порталы при помощи профилей, т.е. чтобы собирать для pluto надо указать соответствующий профиль.

cd hello-portlet
mvn -Ppluto package

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

Если вместо pluto указать liferay-cdi или liferay-spring, то будет собран проект с конфигурациями и библиотеками для портала Liferay с одной из реализаций системой управления зависимостями. Т.е. в коде для нас может почти ничего не меняться т.к. у них есть унификация интерфейсов, а управляться зависимости в работающем приложении будут разными подсистемами.

После выполнения команды в target/’е привычно для Java-разработчика окажется .war файл.

Задеплоить приложение на портал мы можем просто скопировав .war архив в подкаталог сервера webapps.

Где-то через секунду в логах появятся отчеты об обнаруженном дистрибутиве и ходе его установки.

Добавим тестовую страницу и разместим на ней наш портлет.

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

Портлет это то, что соответствует окошечку на портале, которое, к слову, может не иметь декораций и рисовать любой блок разметки, данных или ресурс. Кроме состояния VIEW соответствующего чаще всего некоему render методу оно может также иметь и другие, такие как EDIT, HELP и вообще любые произвольно добавленные. Но разумным наверное могло бы быть и соответствие “один экран” == “один портлет”. Т.к. портлеты городить с появлением аннотаций стало гораздо проще. Порталы дают возможность решать эту непростую задачу стандартизированным образом. Сейчас многие порталы поддерживают ЧПУ (человекопонятный адрес в строке браузера) мапинг, а также имеют интерфейсы для обратного биндинга ссылок, что может показаться уже не так просто как захардкодить все адреса в роутах, но более совершенно в целом.

Созданный в результате каталог hello-portlet/ можно импортировать как обычный maven проект в IntelliJ IDEA. Замечательной особенностью является также и то, что мы можем использовать gradle в качестве системы сборки, т.к. его конфигурация тоже была сгенерирована. Но сегодня останемся пока на maven.

Открыв проект мы видим, что перед нами очень похожее на привычное MVC приложение на джаве.

Теперь вместо, т.е. вместе с конфигурациями используются аннотации которыми можно декорировать методы практически произвольно, связывание при этом с абстракцией портлетов происходит за счет атрибутов portletName или portletNames. Это дает несколько больше гибкости чем раньше, когда приходилось наследоваться от некоего Portlet или реализовывать его интерфейс.

Декораторы выглядят вполне знакомо:

@Inject - привычно завайрит класс-зависимость или сервис

@RenderMethod - метод для проталкивания данных в шаблон

@ActionMethod - метод для обработки GET, POST запросов “на бекенд”

@ServeResourceMethod - метод для получения данных с бекенда например в JSON для загрузки в фронтенд технологию

Для экшин-методов идентификация происходит также по actionName, а ресурсов по id. 

Шаблоны и другие фронтенд ресурсы могут лежать в положенном по WAR лейауту месте, т.е. например 

src/main/webapp/resources

Для рендер метода можно указать какой шаблон рендерить прямо параметром

@RenderMethod(portletNames = "HelloPortlet", include = "/WEB-INF/jsp/helloView.jsp")

Чтобы в шаблоне получить ссылку на ресурсный или экшин метод используют теги например так

<portlet:actionURL var="placeOrderURL" name="placeOrder"></portlet:actionURL>

или вот так

<portlet:resourceURL var="getSettingsURL" id="getSettings"></portlet:resourceURL>

Это можно прямо завернуть в джаваскрипт коде в строку и использовать в rest запросах на сервер.

Значения прокидываются через request.setAttribute и доступны как переменные сразу если вы используете JSTL (т.е. прописана в зависимостях и в теглибах) или через доставание из контекста если просто JSP

<%
   String ctxPath = request.getContextPath();
   List<String> settings = (List<String>) renderRequest.getAttribute("settings");
%>

Тут надо осознавать, что портлет это более универсальное и переиспользуемое явление по сравнению с обычным монолитным приложением или даже модулем. И ваш портлет должен быть готов к тому, чтобы оказаться добавленным на любую страницу и даже иногда в количестве более одного экземпляра. Он не должен ломаться или глючить от такого пользовательского волюнтаризма.

Чтобы код разных портлетов и экземпляров не конфликтовал между собой и работал без перехадркоживания значений под каждый сервер, следует использовать <portlet:namespace> и contextPath для глобальных идентификаторов в браузере т.е. id и имен переменных, а внутрепортлетную логику структурировать так чтобы она не завязывалась на глобальное, иначе говоря, была модульной и могла интегрироваться в среду.

Если ваш фронтенд фреймворк поддерживает независимую работу нескольких приложений на странице или нескольких не предполагается, то можно спокойно прикручивать его. Для лайфрея есть целый набор примеров приложений с использованием React, Angular, Vue и других популярных фреймворков. Но я бы рекомендовал использовать для разработки фронтенда технологии веб-компонентов, т.к. они позволяя использовать современные технологии модульности дают возможность структурировать код разделенным на компоненты. Моя библиотечка skinny-widgets (https://www.npmjs.com/package/skinny-widgets) дает некоторый набор готовых виджетов написанных на веб-компонентах и поддерживает современные стандарты разработки включая модульность и темизацию.   

Портлеты установленные в портал работают в некоторой изоляции друг от друга даже когда находятся на одной странице и для получения данных из GET параметров запроса в контексте портлета надо объявлять public render parameters в аннотациях портлета или метода

@PortletConfiguration(portletName="portlet1", publicParams = {"categoryId"},
 … { другие параметры } 
)

При этом для POST данных такого делать не нужно, переданный параметр с бекенд стороны можно получить через такой интерфейс.

RenderURL renderURL = resp.createRenderURL();
MutableRenderParameters renderParams = renderURL.getRenderParameters();
String idString = renderParams.getValue(name);

На момент проверки эта возможность не работала еще в Liferay и для кроссплатформенности я делал обертку с фолбеком (в конце статьи будет ссылка на проект с этим кодом) на выдергивание из контекста как раньше, ссылку на которое вы найдете в конце статьи.

А для данных multipart форм есть специальный метод

Part part = request.getPart("image");

Портлеты “из коробки” поддерживают интерационализацию, т.е вы можете загружать переводы под локаль из ресурсных файлов, которые также можно определить в @PortletConfiguration, там же определяются и состояния портлетов и другие их параметры.

Кроме того, в портлетах есть понятие PortletPreferences, т.е. интерфейса для настроек портлета которые могут изменяться администраторами в соответствующих диалогах. В Apache Pluto эти параметры не сохраняются между рестартами, но в Liferay очень активно используются и там даже есть свои диалоговые примочки для работы с ними.

Все это декларируется там же в @PortletConfiguration и соответствует содержимому portlet.xml, которым делалась конфигурация для предыдущих версий портлетов и даже объединяется с ним если вы решили использовать такие конфигурации.

Если возникают трудности вида “как это сделать для портала”, очень полезно будет посмотреть примеры код которых находится в подкаталоге demo/ репозитория портала с которого мы начали изучение. Там вы найдете ответы на типовые вопросы вроде “как работать с формами и аплоадом ?”, “как контролируемо подключать фронтенд файлы ?”.

Если выходишь за их рамки, т.е. прикручиваешь разные библиотеки и фреймворки, бывает вылазят разного рода ошибки, одна из которых касается встроенного инжектора weld, решение этой проблемы заключается в том чтобы скопировать джарку jandex версии например 1.2.2.Final в подкаталог lib/ сервера. 

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

Пример готового модульного проекта интернет-магазинчика построенного с использованием сервисной архитектуры можно найти тут: http://bitbucket.org/finistmart

Он состоит из нескольких портретных проектов и сервиса на JAX-RS инкапсулирующего бизнес-логику и работающего как с СУБД MySQL так и c Postgres. 

Предупреждение: я там не делал рефакторинга еще и кодревью проводить не обязательно;) Там есть например дублирование которое уйдет в перспективе в разделяемые библиотеки.

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

Все это добро можно задеплоить в один Pluto или Liferay сервер и вести разработку на относительно скромной по современным меркам конфигурации ПК (мне хватает ноута i7 2Ггц U с 8 Gb RAM на СУБД, сервер приложений даже когда это liferay, среду разработки со всеми этими проектами в дебаге, браузер хром и плеер музыки;) и деплоить в кластер на разные ноды.

Дистрибутив Liferay, который не надо собирать из исходников, а можно просто распаковать и запустить проще всего скачивается из раздела файлов одноименного проекта на sourceforge.net 

В Liferay портлетные варки закидываются в deploy/, а сервелетные в tomcat-x.y.z/webapps и для комфортной разработки в portlet-ext.properties надо добавить параметры (которые можно нагуглить) отключающие различные кеширования. После некоторой настройки, он может оказаться неплохим вариантом и для разработки.




К сожалению, не доступен сервер mySQL