Подтвердить что ты не робот

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

Мне нужно получить ServletContext изнутри @ServerEndpoint, чтобы найти Spring ApplicationContext и найти Bean.

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

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

4b9b3361

Ответ 1

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

Чтобы зафиксировать запрос подтверждения, выполните ServerEndpointConfig.Configurator и переопределите modifyHandshake(). HandshakeRequest здесь доступен как аргумент метода. Вы можете поместить HttpSession в EndpointConfig#getUserProperties(). 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 сервлета. Вы бы в реализации modifyHandshake() лучше сразу извлекали именно эту информацию (обычно изменяемую Javabean), которая вам нужна из сеанса сервлетов или контекста, и вместо этого помещали их в карту свойств пользователя. Если вы этого не сделаете, то вам следует помнить, что сеанс websocket может жить дольше, чем сеанс HTTP. Поэтому, когда вы продолжаете переносить HttpSession в конечную точку, вы можете столкнуться с IllegalStateException при попытке получить к ней доступ, пока он истек.

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

См. также:

Ответ 2

Обновленный код для ответа 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();
        // ...
    }

}

Ответ 3

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

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