I’ve been struggling with getting push working in my Vaadin application on Tomcat for a while now. And it appears that Tomcat’s Comet support is never used.
In Tomcat 6 (using the latest 6.0.37), Atmosphere correctly first attempts to use TomcatCometSupport. But because the request attribute “CometEvent” is never set, it always fails at line 105 in that class, resulting in this error message:
org.atmosphere.cpr.AtmosphereFramework doCometSupport WARNING: Failed using comet support: org.atmosphere.container.TomcatCometSupport, error: Tomcat failed to detect this is a Comet application because context.xml is missing or the Http11NioProtocol Connector is not enabled.
If that's not the case, you can also remove META-INF/context.xml and WEB-INF/lib/atmosphere-compat-tomcat.jar Is the Nio or Apr Connector enabled?
org.atmosphere.cpr.AtmosphereFramework doCometSupport WARNING: Using org.atmosphere.container.BlockingIOCometSupport
It also results in Atmosphere falling back to the generic class BlockingIOCometSupport. Further, any Tomcat Comet-specific code in that class is not executed because, again, the request attribute “CometEvent” is not set.
A similar situation happens with Tomcat 7 (using the latest 7.0.42). While the latest browsers use WebSockets, any browser that doesn’t support it (e.g., IE9) falls back to use streaming. Tomcat7AsyncSupportWithWebSocket is first tried, and the same error occurs for the same reason, resulting in a fallback to Tomcat7BIOSupportWithWebSocket.
In Atmosphere’s code, the “CometEvent” request attribute is set in the class AtmosphereServlet. The equivalent code is not present in Vaadin 7.1.
So for everyone who has spent hours messing with context.xml and nio/bio, it was a huge waste of your time. AFAICT you will always get this error message if you’re using Vaddin (as of 7.1.7) in Tomcat and the browser doesn’t support WebSockets (or with any browser if you’re using @Push(transport=STREAMING)).
In a trivial Vaadin test app running on Tomcat, it’s exactly the same as I described above–the fallback to Comet fails. However the generic fallback classes seem to work ok with that simple application. However, in my application, the generic classes BlockingIOCometSupport and Tomcat7BIOSupportWithWebSocket do not work. For example with IE9, the initial view loads just fine, but as soon as I interact with the application, it just hangs forever. It looks like it’s waiting for a response from the server, but it never updates the UI. Maybe it has to do with the fact that my application uses https, I’m not sure. I still have not been able to figure out exactly why this is happening.
But I needed to come up with a solution. So I was able to make Comet work in Tomcat 7 by making my servlet implement org.apache.catalina.comet.CometProcessor, adapting code found in org.atmosphere.cpr.AtmosphereServlet:
@Override
public void event(CometEvent cometEvent) throws IOException, ServletException {
HttpServletRequest request = cometEvent.getHttpServletRequest();
HttpServletResponse response = cometEvent.getHttpServletResponse();
//Set the request attribute that Atmophere expects in order to use Comet
request.setAttribute(Tomcat7CometSupport.COMET_EVENT, cometEvent);
RequestUtils.checkAndSetLocale(request);
RequestUtils.checkAndSetSecurityContext(request, response);
EventType eventType = cometEvent.getEventType();
if (eventType==EventType.BEGIN) {
//Delegate to Servlet's service method
service(request, response);
response.flushBuffer();
}
if (eventType==EventType.ERROR || eventType==EventType.END) {
log.trace("Closing. eventType={}", eventType);
cometEvent.close();
}
if (webSocketSupported(cometEvent)){
cometEvent.close();
}
}
private boolean webSocketSupported(CometEvent cometEvent) {
// https://github.com/Atmosphere/atmosphere/issues/920
String transport = cometEvent.getHttpServletRequest().getParameter(HeaderConfig.X_ATMOSPHERE_TRANSPORT);
boolean webSocketSupported = (transport != null && transport.equalsIgnoreCase(HeaderConfig.WEBSOCKET_TRANSPORT));
if (!webSocketSupported) {
try {
Enumeration<String> connection = cometEvent.getHttpServletRequest().getHeaders("Connection");
if (connection != null && connection.hasMoreElements()) {
String e = connection.nextElement().toString().split(",");
for (String upgrade : e) {
if (upgrade.trim().equalsIgnoreCase(WEBSOCKET_UPGRADE)) {
webSocketSupported = true;
break;
}
}
}
} catch (Exception ex) {
log.trace("", ex);
}
}
return webSocketSupported;
}
Perhaps I’ve missed something obvious, but if I’m right, this at minimum needs to be better documented. I doubt I’m the only one who’s been banging his head against the wall over this.
Has anyone seen Vaadin/Atmosphere successfully fall back to Comet in Tomcat?
–Darren Evenson