Короче говоря, я не знаю, как уведомить сессионный компонент из компонента. Я нашел грязный хак, который работает, но я не уверен, что есть лучший способ решить эту проблему (или мой грязный хак не грязный :-D).
У каждого веб-сокета в java ee есть идентификатор сеанса. Но этот sessionid не такой, как в jsf, и нет способа для простого отображения. В моей среде у меня есть веб-страница jsf, базовый компонент сессионной области и веб-сокет, который подключен к внешней службе через jms. Когда страница jsf загружена и веб-сокет также подключен к браузеру, компонент поддержки отправляет запрос во внешнюю службу. Когда я получил асинхронное ответное сообщение через jms, я не знаю, какой веб-сокет связан со страницей jsf / компонентом поддержки, отправляющим запрос.
Чтобы решить эту проблему с частично грязным взломом, я написал класс-посредник для приложения.
@Named
@ApplicationScoped
public class WebsocketMediator {
@Inject
private Event<UUID> notifyBackingBeans;
private Integer newSequenceId=0;
// I need this map for the dirty hack
private Map<UUID, BackingBean> registrationIdBackingBeanMap;
private Map<Integer, UUID> sequenceIdRegistrationIdMap;
registrationIdWebSocketMap = new ConcurrentHashMap<>();
public UUID register(BackingBean backingBean) {
UUID registrationId = UUID.randomUUID();
registrationIdSequenceMap.put(registrationId, new HashSet<>());
registrationIdBackinBeanMap.put(registrationId, backingBean);
}
public Integer getSequenceId(UUID registrationId) {
sequenceId++;
sequenceIdRegistrationIdMap.put(sequenceId, registrationId);
registrationIdSequenceMap.get(registrationId).add(sequenceId);
return sequenceId;
}
// this is called from the ws server enpoint
public void registerWebsocket(UUID registrationId, Session wsSession) {
registrationIdWebSocketMap.put(registrationId, wsSession);
websocketRegistrationIdMap.put(wsSession.getId(), registrationId);
notifyBackingBeans.fire(registrationId); // This does not work
SwitchDataModel switchDataModel = registrationIdSwitchDataModelMap.get(registrationId);
if (backingBean != null) {
backingBean.dirtyHackTrigger();
}
}
public void unregisterWebsocket(String wsSessionId) {
...
}
}
Поддерживающий компонент вызывает метод регистрации и получает уникальный случайный идентификатор регистрации (uuid). Регистрационный идентификатор помещается в таблицу jsf как скрытый атрибут данных (f: passTrough). Когда веб-сокет подключен, в браузере вызывается функция ws.open, которая отправляет идентификатор регистрации через веб-сокет в класс конечных точек сервера веб-сокетов. Класс конечной точки сервера вызывает метод посредника public void registerWebsocket(UUID registrationId, Session wsSession)
, и идентификатор регистрации сопоставляется. Когда резервный бин истекает, я вызываю незарегистрированный метод из аннотированного метода @PreDestroyed
. Каждый раз, когда внешняя система вызывается через jms, я помещаю идентификатор последовательности в полезную нагрузку. Идентификатор последовательности зарегистрирован в классе медиатора. Каждый раз, когда внешняя система отправляет сообщение, я могу найти правильный посредник в посреднике, чтобы пропустить это сообщение через правильный подходящий браузер.
Теперь система может получать асинхронные события через внешнюю систему, но компонент поддержки этого не знает. Я попытался отправить регистрационный идентификатор в событии cdi бэк-бину области сеанса, но событие так и не достигло наблюдателя в бэк-бэк области сеанса. Так что я понял эту болтовню с грязным хаком. Я помещаю экземпляр каждого компонента поддержки с идентификатором регистрации в качестве ключа в карту в посреднике в методе регистрации. Я поместил в public void registerWebsocket(UUID registrationId, Session wsSession)
грязный триггерный вызов бэк-компонента. Есть ли лучшее решение?
Я использую Wildfly 13 с CDI 1.2.
Заранее спасибо!