Доступ к ServletContext и HttpSession в @OnMessage JSR-356 @ServerEndpoint

мне нужно ServletContext внутри @ServerEndpoint для того, чтобы найти Spring ApplicationContext и поиск Боба.

на данный момент мой лучший подход-связать этот компонент в контексте именования JNDI и найти его в Endpoint. Любое лучшее решение приветствуется.

Я также ищу разумный способ синхронизации сервлета HttpSession С websocket в Session.

3 ответов


сервлет HttpSession в JSR-356 доступно по HandshakeRequest#getHttpSession() который, в свою очередь, доступен, когда запрос рукопожатия сделан прямо перед @OnOpen of a @ServerEndpoint. The ServletContext в свою очередь доступен только через HttpSession#getServletContext(). Это две птицы одним выстрелом.

для того, чтобы захватить запрос рукопожатия, реализовать ServerEndpointConfig.Configurator и переопределить modifyHandshake() метод. The HandshakeRequest здесь доступно как аргумент метода. Вы можете поставить HttpSession на EndpointConfig#getUserProperties(). The EndpointConfig в свою очередь доступен в качестве метода аргумент @OnOpen.

вот пример стартового ServerEndpointConfig.Configurator реализация:

public class ServletAwareConfig extends ServerEndpointConfig.Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        config.getUserProperties().put("httpSession", httpSession);
    }

}

вот как вы можете использовать его, Примечание configurator на @ServerEndpoint:

@ServerEndpoint(value="/your_socket", configurator=ServletAwareConfig.class)
public class YourSocket {

    private EndpointConfig config;

    @OnOpen
    public void onOpen(Session websocketSession, EndpointConfig config) {
        this.config = config;
    }

    @OnMessage
    public void onMessage(String message) {
        HttpSession httpSession = (HttpSession) config.getUserProperties().get("httpSession");
        ServletContext servletContext = httpSession.getServletContext();
        // ...
    }

}

как подсказка дизайна, лучше всего сохранить ваш @ServerEndpoint полностью свободен от зависимостей API сервлетов. Ты в the modifyHandshake() реализация лучше сразу извлечь именно это информация (обычно изменяемый Javabean) вам нужно из сеанса сервлета или контекста и поместить их в карту свойств пользователя. Если вы этого не сделаете, то вы должны иметь в виду, что сеанс websocket может жить дольше, чем сеанс HTTP. Поэтому, когда вы все еще носите HttpSession в конечную точку, затем вы можете столкнуться с IllegalStateException при попытке доступа к нему во время истeкший.

если у вас есть CDI (и, возможно, JSF), вы можете получить вдохновение от исходного кода OmniFaces <o:socket> (ссылки в самом низу витрины).

Читайте также:


обновленный код для ответа BalusC, метод onOpen должен быть украшен @OnOpen. Тогда больше нет необходимости расширять класс Endpoint:

@ServerEndpoint(value="/your_socket", configurator=ServletAwareConfig.class)
public class YourSocket {

    private EndpointConfig config;

    @OnOpen
    public void onOpen(Session websocketSession, EndpointConfig config) {
        this.config = config;
    }

    @OnMessage
    public void onMessage(String message) {
        HttpSession httpSession = (HttpSession) config.getUserProperties().get("httpSession");
        ServletContext servletContext = httpSession.getServletContext();
        // ...
    }

}

Я попробовал ответ BalusC на Tomcat (версии 7.0.56 и 8.0.14). В обоих контейнерах параметр запроса modifyHandshake не содержит HttpSession (и, следовательно, никакого servletContext). Поскольку мне нужен был контекст сервлета только для доступа к "глобальным" переменным (то есть к глобальному веб-приложению), я просто сохранил эти переменные в обычном статическом поле класса holder. Это неэлегантно, но сработало.

Это Оки, как ошибка в этой конкретной версии tomcat-есть кто-нибудь там тоже видели это?