Статьи

Взгляд на грядущую поддержку JSF 2.3 Push

Как упоминалось в предыдущих статьях, в следующий выпуск JavaServer Faces (Mojarra) добавлен ряд улучшений. JSF 2.3 планируется к выпуску с Java EE 8 в 2017 году, но вы можете получить некоторые из усовершенствований и обновлений JSF для тестирования уже сейчас, собрав исходные коды или выполнив этапный выпуск.

Одним из таких усовершенствований API является добавление односторонней (сервер-клиент) push-связи на основе f:websocket тег f:websocket и Push API. Команда OmniFaces разработала решение JSF на основе o: socket , которое является частью служебной библиотеки OmniFaces. В частности, члены экспертной группы JSR 372 Бауке Шольц и Арджан Тиймс внесли это и многие другие улучшения и исправления в кодовую базу Mojarra.

Патч, который включает поддержку f:websocket еще не был применен к ветке Mojarra 2.3, но вы можете получить патч из выпуска JAVASERVERFACES_SPEC_PUBLIC-1396 . Прежде чем применять патч к вашему локальному клону Mojarra, вы должны обязательно обновить свои источники из центральной ветки 2.3, чтобы убедиться, что у вас установлены последние обновления. Использование простое, очень похоже на хорошо документированную функцию o:socket на сайте OmniFaces, выполните следующие шаги, чтобы использовать f:websocket .

Сначала добавьте

1
javax.faces.ENABLE_WEBSOCKET_ENDPOINT

параметр context для web.xml вашего приложения и установите значение true.

1
2
3
4
<context-param>
    <param-name>javax.faces.ENABLE_WEBSOCKET_ENDPOINT</param-name>
    <param-value>true</param-value>
 </context-param>

Код на стороне клиента

На вашем клиенте (представление JSF) добавьте тег f:websocket и укажите канал, к которому вы хотите подключиться. Необходимо также указать прослушиватель onmessage который будет выполнять указанную функцию JavaScript после получения сообщения. Также может быть указан необязательный атрибут onclose , позволяющий указанной функции JavaScript выполняться при закрытии соединения. В следующем примере мы указываем, что сокет будет подключаться к каналу с именем «duke» вместе с прослушивателем dukeSocketListener именем dukeSocketListener :

1
<f:websocket channel="duke" onmessage="dukeMessageListener"/>

Слушатель onmessage может быть вызван с тремя параметрами (объект JSON push-сообщения, имя канала, событие сообщения). Если вы просто хотите передать сообщение, оно может выглядеть примерно так:

1
2
3
function dukeMessageListener(message) {
        PF('broadcastGrowl').show(message);
}

Если onclose дополнительный прослушиватель onclose , соответствующая функция может принимать три параметра (код причины закрытия — целое число, имя канала, событие сообщения), но требуется только первый.

В большинстве случаев предполагается отправить сообщение с сервера, чтобы уведомить все клиентские представления, имеющие одинаковую websocket канала websocket . В f:websocket есть необязательный атрибут области f:websocket , для которого можно установить значение «сессия», и это ограничит сообщения всеми представлениями клиентов с одним и тем же каналом веб-сокета только в текущем сеансе.

Наконец, необязательный атрибут port может быть установлен для указания номера порта TCP, отличного от порта HTTP, если это необходимо.

Код на стороне сервера

Поскольку мы планируем отправить сообщение с сервера всем подключенным клиентам, давайте посмотрим на код на стороне сервера. Новый PushContext может быть PushContext в любой артефакт CDI путем включения аннотации @Push , и имя контекста может либо соответствовать имени канала, либо необязательный атрибут channel может быть указан в аннотации @Push для указания канала, к которому сообщение должно быть передано.

1
2
3
4
5
6
@Inject @Push
    private PushContext duke;
...
public void sendMessage(Object message){
    duke.send(message);
}

Сообщение будет закодировано как JSON и доставлено в аргумент сообщения функции JavaScript на клиенте, который указан для атрибута onmessage в f:websocket . В качестве сообщения можно отправить любой тип контейнера, будь то обычный String, JavaBean, Map, Collection и т. Д.

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

Предположим, что у нас есть административная консоль для нашего веб-приложения, и мы хотим предоставить администраторам средства оповещения клиентов о чем-то. Административная консоль может иметь текстовую область для ввода сообщения вместе с commandButton, чтобы вызвать отправку сообщения как такового.

1
2
<h:inputText id="pushMessage" value="#{testBean.pushMessage}"/>
<h:commandButton action="#{testBean.sendAdminMessage}" value="Send Message"/>

Тогда у класса JSF-контроллера testBean будет метод sendAdminMessage , который принимает сообщение, хранящееся в pushMessage , и отправляет его в наш метод sendMessage .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@Inject @Push
    private PushContext duke;
 
...
 
public void sendAdminMessage(){
    sendMessage(pushMessage);
    FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Message has been broadcasted"));
}
 
...
 
public void sendMessage(Object message){
 
    duke.send(message);
}

Любой клиент, который получит сообщение, должен содержать тег f:websocket , указывающий на канал duke . Клиент также должен включать как минимум функцию JavaScript, которая будет вызываться при получении сообщения.

01
02
03
04
05
06
07
08
09
10
<f:websocket channel="duke" onmessage="dukeMessageListener"/>
 
 
<p:growl id="messages"/>
 
 
function dukeMessageListener(message) {
        facesmessage.severity = 'info';
        PF('broadcastGrowl').show(message);
}

В этом конкретном примере компонент сообщения рычания PrimeFaces будет обновляться при получении сообщения.

JSF 2.3 хорошо развивается благодаря отличному вкладу членов экспертной группы JSR 372.