Обмен данными между JSF @ViewScoped и WebSocket @ServerEndpoint

Я использую веб-сокеты JSR356 и хочу добавить некоторые функции и значения в класс конечной точки сервера вместо создания еще одного ManagedBean.

Также я хотел бы сохранить свойства bean-компонента между запросами, поэтому я аннотировал свою конечную точку сервера аннотацией @ViewScoped. И теперь это выглядит так:

@Named
@ViewScoped
@ServerEndpoint(value = "/session", encoders = ChatMessageEncoder.class, decoders = ChatMessageDecoder.class)
public class ChatEndpoint implements Serializable {

    @EJB
    private LanguageHelper languageHelper;
    private String language;

    public void filterByLanguage() {
        if (language == null)
            language = "US";
    }

    @OnOpen
    public void open(final Session session) throws IOException, EncodeException {
        // ...
    }

    @OnMessage
    public void onMessage(final Session session, final ChatMessage chatMessage) {
        // ...
    }

    @OnClose
    public void onClose(Session session) throws IOException {
        // ...
    }

    // getters & setters
}

На моем view xhtml я пытаюсь обновить свойство language с помощью ajax.

 <h:form>
    <h:outputLabel value="Select languages you want to practice" for="languages"/>
    <h:selectOneMenu value="#{chatEndpoint.language}">
        <f:selectItems value="#{chatEndpoint.languageHelper.languages}" var="l"
                       itemLabel="#{l.language}" itemValue="#{l.code}"/>
        <f:ajax event="change" listener="#{chatEndpoint.filterByLanguage}" render="@form" execute="@this"/>
    </h:selectOneMenu>
</h:form>

Сначала, когда ajax вызывает метод filterByLanguage, значение устанавливается в поле language, но после, когда я вызываю метод, аннотированный с @OnOpen по javascript, тогда language равно нулю. Я думал, что viewsocoped должен сохранять состояние между запросами. Кто-нибудь может объяснить такое поведение? Заранее спасибо!


person user3127896    schedule 17.03.2015    source источник


Ответы (1)


В основном вы получили 2 независимо созданных экземпляра класса. Один как управляемый компонент CDI через @Named, а другой как конечная точка веб-сокета JSR356 через @ServerEndpoint. Это не оказалось единым общим экземпляром, как вы ожидали. Эти двое ничего не знают друг о друге и ничем не делятся.

Затем возникает вторая проблема: @ServerEndpoint не имеет никакого представления о текущем состоянии представления JSF. Эта информация нигде не доступна в запросе WS. Ближайшее, что вы можете получить, - это сеанс HTTP. Как получить HttpSession в @ServerEndpoint подробно описано в этом ответе: Доступ к ServletContext и HttpSession в @OnMessage @ServerEndpoint JSR-356.

Вы, наверное, уже знаете, что вы можете получить доступ к атрибутам HttpSession на стороне JSF через ExternalContext#getSessionMap(). Если вы создаете какой-то уникальный токен на стороне JSF, например. UUID.randomUUID().toString() и использовать его в качестве ключа атрибута сеанса, а затем передать через представление JSF веб-сокету в качестве URL-адреса или параметра запроса, тогда веб-сокет может просто использовать его для поиска связанных данных в общем сеансе HTTP.

person BalusC    schedule 17.03.2015