I create a sample webapp using Guice-servlets and websocket in tomcat, now once guice filter is used websocket stop working
Basic information:
In my web.xml, i initialized the Guiceservlet using 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
Code which binds all request /*
to 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
code which just respond with a String
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);
}
}
Also i have a @ServerEndPoint for Websocket
@ServerEndpoint(value = "/websocket/chat2")
public class WebSocket{
....
@OnOpen
public void start(Session session) {
System.out.println("Staring:"+this);
}
....
}
Observations:
- Now if i run the app and hit http://app:8080/test it returns
SUCCESS
- But if i try to connect to websocket using ws://app:8080/websocket/chat2 it fails
Now if i comment
serve("/*").with(MyDispatcher.class);
basically if we switch off guice routing the websocket starts to workIf i switch off guice-servlet but add a servlet mapping in web.xml like below websocket still works
< servlet-mapping >
< servlet-name > HelloWorld< / servlet-name >
< url-pattern > /* < / url-pattern >
< / servlet-mapping >
What am i missing or doing wrongly?
EDIT:
Observation-conti:
- What i did was defined a simple filter which just respond with
FILTER
.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
response.getOutputStream().print("FILTER"); }
and changed my web.xml to
<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>
Now hitting the http://localhost:8080/app/x
return FILTER
as expected.
But trying to connect with websocket fail as the request shows something like this.
I also noticed that as i change the String MyFilter
return the content length in response changes , which meaning the request reached MyFilter
before tomcat handled it for websocket.
I changed the web.xml to below and guice and websocket are working fine now.. so i think Guice is not honoring the WsFilter that registered after the 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-servlet-4.0
That also looks like a Guice issue to me (as already mentioned in comments). Using servlets and WebSockets in same application should not be a problem, even with a servlet mapping that covers
/*
.2 relevant things about servlets and filters:
So, if WsFilter is first, it will intercept the request first, then check if it's a WebSocket upgrade request.
If it's indeed a WebSocket connection, the filter will not pass it on to the rest of the chain.
If it's another type of request (GET, POST...), it will pass it on, and then Guice will do its thing.
(so you found a first solution here)
If Guice filter is first, AND you use
serve("/*")...
, then it breaks your WS.If you comment out
serve("/*")...
, then it does not matter if Guice filter is first or not, WsFilter can even be absent: your WS can be reached (which establishes GuiceFilter alone is OK).So Guice has its own "layer of interception" above servlet mappings, and I think that's what breaks WebSockets. I don't know if there is a bug or anything to fix in Guice (I mean probably, but don't know what exactly), but you can specify exceptions to Guice (unlike servlet mappings in
web.xml
).Replace this:
with this:
With that, you can keep the servlet mapping on
/*
, and removeWsFilter
as it is not needed. (Tested, works for me)So that's a second solution, that has the advantage that it also allows to specify exceptions for non-WebSocket stuff.