Tomcat: использование сервлета и веб-сокета (jsr356) в одном веб-приложении

Я создаю образец веб-приложения, используя сервлеты Guice и веб-сокет в tomcat, теперь, когда используется фильтр guice, веб-сокет перестает работать

Основная информация:

В моем web.xml я инициализировал Guiceservlet, используя GuiceBasedListener

<web-app>
     <listener>
        <listener-class>test.GuiceBasedListener</listener-class>
    </listener>          
    <filter>
        <filter-name>guiceFilter</filter-name>
        <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>guiceFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

GuieBasedListener Код, который связывает все запросы /* с MyDispatcher

public class GuiceBasedListener extends GuiceServletContextListener {
    protected Injector getInjector() {
        return Guice.createInjector( new ServletModule() {
            @Override
            protected void configureServlets() {
                bind(MyDispatcher.class).asEagerSingleton();
                serve("/*").with(MyDispatcher.class);//////IMPORTANT LINE//
            }
        });}}

MyDispatcher код, который просто отвечает строкой

public class MyDispatcher extends HttpServlet {    
    @Inject private Injector injector;
    public MyDispatcher() {}    
    public void service(ServletRequest req, ServletResponse resp) throws IOException, ServletException {
        resp.getOutputStream().print("SUCCESS:" + req);
    }
}

Также у меня есть @ServerEndPoint для Websocket

@ServerEndpoint(value = "/websocket/chat2")
public class WebSocket{
....
    @OnOpen
    public void start(Session session) {        
        System.out.println("Staring:"+this);
   }
....
}

Наблюдения:

  1. Теперь, если я запущу приложение и нажму http://app:8080/test, оно вернет SUCCESS
  2. Но если я попытаюсь подключиться к веб-сокету с помощью ws://app:8080/websocket/chat2, это не удастся.
  3. Теперь, если я прокомментирую serve("/*").with(MyDispatcher.class);, в основном, если мы отключим маршрутизацию guice, веб-сокет начнет работать

  4. Если я отключу guice-servlet, но добавлю отображение сервлета в web.xml, как показано ниже, websocket все еще работает

    < servlet-mapping > < servlet-name > HelloWorld< / servlet-name > < url-pattern > /* < / url-pattern > < / servlet-mapping >

Что я упускаю или делаю неправильно?


ИЗМЕНИТЬ:

Наблюдение-продолжение:

  1. Что я сделал, так это определил простой фильтр, который просто отвечает FILTER .

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { response.getOutputStream().print("FILTER"); }

и изменил мой web.xml на

<web-app>           
    <filter>
        <filter-name>myFilter</filter-name>
        <filter-class>test.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>myFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>       
</web-app>

Теперь, нажав http://localhost:8080/app/x, вернитесь FILTER, как и ожидалось. Но попытка подключения с помощью веб-сокета не удалась, поскольку запрос показывает что-то вроде этого. Я также заметил, что когда я изменяю строку MyFilter, возвращаю длину содержимого в ответных изменениях, что означает, что запрос достиг MyFilter до того, как tomcat обработал его для веб-сокета.

введите описание изображения здесь

  1. Я изменил web.xml на приведенный ниже, и теперь guice и websocket работают нормально.. поэтому я думаю, что Guice не соблюдает WsFilter, зарегистрированный после GuiceFilter.

    <filter>
    <filter-name>myFilter</filter-name>
    <filter-class>org.apache.tomcat.websocket.server.WsFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>myFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping> 
    
     <filter>
        <filter-name>guiceFilter</filter-name>
        <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>guiceFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

TOMCAT 8.0, Window 7, Java 1.7, Guice 4.0, Guice-сервлет-4.0


person Bhuvan    schedule 27.05.2017    source источник
comment
Как я могу сказать tomcat, чтобы он сначала рассматривал URL-адрес serverEndPoint для маршрутизации запроса, прежде чем сопоставлять шаблоны URL-адресов фильтра? Честно говоря, Tomcat уже делает это. Вы просто неверно истолковали причину вашей реальной проблемы. Я рекомендую переписать вопрос, чтобы подробно рассказать о наблюдаемых симптомах реальной проблемы, вместо того, чтобы спрашивать, как решить ошибочно предполагаемую возможную причину. Я полагаю, что в GuiceFilter есть просто ошибка и требуется простое исправление, но неясно, что именно, пока вы не уточните о фактически наблюдаемых симптомах.   -  person BalusC    schedule 30.05.2017
comment
@BalusC вы правы ... я переписал вопрос   -  person Bhuvan    schedule 30.05.2017
comment
Но если я попытаюсь подключиться к веб-сокету с помощью ws://app:8080/websocket/chat2, произойдет сбой Как именно это произойдет? Что именно происходит, а что нет и что происходит вместо этого?   -  person BalusC    schedule 30.05.2017
comment
@BalusC Добавил интересное наблюдение   -  person Bhuvan    schedule 30.05.2017
comment
@BalusC Я думаю, что фильтры в web.xml зарегистрированы до WsFilter tomcat. Просто предположение?   -  person Bhuvan    schedule 30.05.2017


Ответы (1)


Это также похоже на проблему с Guice (как уже упоминалось в комментариях). Использование сервлетов и веб-сокетов в одном приложении не должно быть проблемой, даже если сопоставление сервлетов охватывает /*.

2 важные вещи о сервлетах и ​​фильтрах:

Таким образом, если WsFilter находится первым, он сначала перехватит запрос, а затем проверить, является ли это запросом на обновление WebSocket.
Если это действительно подключение WebSocket, фильтр не передаст его остальной части цепочки< /em>.
Если это другой тип запроса (GET, POST...), он передаст его, а затем Guice сделает свое дело.

(так что вы нашли первое решение здесь)

Если фильтр Guice стоит первым, И вы используете serve("/*")..., то он ломает ваш WS.

Если вы закомментируете serve("/*")..., то не имеет значения, является ли фильтр Guice первым или нет, WsFilter может даже отсутствовать: ваш WS может быть достигнут (что устанавливает один только GuiceFilter в порядке).

Итак, Guice имеет свой собственный «уровень перехвата» над сопоставлениями сервлетов, и я думаю, что это то, что ломает WebSockets. Я не знаю, есть ли ошибка или что-то, что нужно исправить в Guice (я имею в виду, вероятно, но не знаю, что именно), но вы можете указать исключения для Guice (в отличие от сопоставлений сервлетов в web.xml ).
Замените это:

serve("/*").with(TestServlet.class);

с этим:

serveRegex("/(?!websocket/).*").with(TestServlet.class);
// Regex that accepts /.* but excludes /websocket/.*

При этом вы можете сохранить отображение сервлета на /* и удалить WsFilter, так как он не нужен. (Проверено, у меня работает)

Итак, это второе решение, которое имеет то преимущество, что оно также позволяет указывать исключения для вещей, отличных от WebSocket.

person Hugues M.    schedule 31.05.2017