websocketjava-8cometdtomcat8.5

"Unknown Bayeux Transport" for websocket transport running cometd on Tomcat


I'm trying to use CometD with websocket transport in my webapp, but the handshake with such transport fails with error "Unknown Bayeux Transport", and CometD always fallback to long-polling.

I'm using

Here is the web.xml

<servlet>
  <servlet-name>cometd</servlet-name>
  <servlet-class>org.cometd.server.CometDServlet</servlet-class>
  <init-param>
    <param-name>jsonContext</param-name>
    <param-value>com.myapp.MyAppJettyJSONContextServer</param-value>
  </init-param>
  <init-param>
    <param-name>allowedTransports</param-name>
    <param-value>websocket,long-polling</param-value>
  </init-param>
  <init-param>
    <param-name>transports</param-name>
    <param-value>org.cometd.websocket.server.JettyWebSocketTransport,org.cometd.server.transport.AsyncJSONTransport</param-value>
  </init-param>
  <init-param>
    <param-name>ws.cometdURLMapping</param-name>
    <param-value>/cometd/*</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
  <async-supported>true</async-supported>
</servlet>
<servlet-mapping>
  <servlet-name>cometd</servlet-name>
  <url-pattern>/cometd/*</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>configuration</servlet-name>
    <servlet-class>com.myapp.MyAppConfigurationServlet</servlet-class>
    <init-param>
      <param-name>SERVER_URL</param-name>
      <param-value>http://localhost:8380/myapp/cometd</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>

<filter>
  <filter-name>cross-origin</filter-name>
  <filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
  <async-supported>true</async-supported>
</filter>
<filter-mapping>
  <filter-name>cross-origin</filter-name>
  <url-pattern>/cometd/*</url-pattern>
</filter-mapping>

Here is the code of MyAppConfigurationServlet.java

public class MyAppConfigurationServlet extends GenericServlet
{

    private static final long serialVersionUID = 6918716199131599352L;

    private static final Logger log = LoggerFactory.getLogger(MyAppConfigurationServlet.class);

    public static final String PUSH_SERVICE = "pushService";

    public static final String CONFIG_SERVER_URL = "SERVER_URL";

    @Override
    public void init() throws ServletException
    {
        WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        BayeuxServer bayeux = (BayeuxServer) getServletContext().getAttribute(BayeuxServer.ATTRIBUTE);
        String serverUrl = getInitParameter(CONFIG_SERVER_URL);
        log.debug("Creating PushService at url {}", serverUrl);
        PushService pushService = new PushService(bayeux, serverUrl);
        sharePushService(ctx, pushService);
    }

    private void sharePushService(WebApplicationContext ctx, PushService pushService)
    {
        try
        {
            PushServiceHolder container = (PushServiceHolder) ctx.getBean(PUSH_SERVICE);
            container.setPushService(pushService);
        }
        catch (BeansException e)
        {
            log.warn("Cannot find the bean {});
            log.trace("Error!", e);
        }
    }

    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException
    {
        throw new ServletException();
    }

}

where PushServiceHolder is a simple spring bean that makes available the PushService to the other beans.

Here is the client implementation

Map<String, Object> options = new HashMap<>();
JettyJSONContextClient jsonContext = new MyAppJettyJSONContextClient();
options.put(ClientTransport.JSON_CONTEXT_OPTION, jsonContext);

HttpClient httpClient = new HttpClient();
httpClient.setMaxConnectionsPerDestination(2);
httpClient.setConnectTimeout(60000);
httpClient.start();

WebSocketClient webSocketClient = new WebSocketClient(httpClient);
webSocketClient.start();
ClientTransport wsTransport = new JettyWebSocketTransport(options, null, webSocketClient);
BayeuxClient client = new BayeuxClient(url, wsTransport, longPollingTransport);
client.handshake();
client.waitFor(5000, BayeuxClient.State.CONNECTED);

The handshake is never successful for websocket transport, with a 404 http error code, so CometD fallbacks on long-polling.

Debugging into org.cometd.server.CometDServlet I found this part

@Override
protected void service(HttpServletRequest request, HttpServletResponse     response) throws ServletException, IOException {
    if ("OPTIONS".equals(request.getMethod())) {
        serviceOptions(request, response);
        return;
    }

    AbstractHttpTransport transport = _bayeux.findHttpTransport(request);
    if (transport == null) {
        response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unknown Bayeux Transport");
    } else {
        transport.handle(request, response);
    }
}

The org.cometd.websocket.server.JettyWebSocketTransport is not an AbstractHttpTransport, so it will never be taken into account by this code, even if it has been configured correctly as allowed transport.
Thus the handshake request for the websocket transport always ends with the error "Unknown Bayeux Transport".

I've used the CometD documentation here and here to create the web.xml.
I would like to take a look to the CometD maven primer, as advised many times by the CometD team, but the command

$ mvn org.apache.maven.plugins:maven-archetype-plugin:2.4:generate -DarchetypeCatalog=http://cometd.org 

returns this error

[WARNING] Error reading archetype catalog http://cometd.org
org.apache.maven.wagon.TransferFailedException: URI does not specify a valid host name: http:/cometd.org
at org.apache.maven.wagon.providers.http.wagon.shared.AbstractHttpClientWagon.fillInputData (AbstractHttpClientWagon.java:1069)
at org.apache.maven.wagon.providers.http.wagon.shared.AbstractHttpClientWagon.fillInputData (AbstractHttpClientWagon.java:963)
at org.apache.maven.wagon.StreamWagon.getInputStream (StreamWagon.java:126)
at org.apache.maven.wagon.StreamWagon.getIfNewer (StreamWagon.java:88)
at org.apache.maven.wagon.StreamWagon.get (StreamWagon.java:61)
at org.apache.maven.archetype.source.RemoteCatalogArchetypeDataSource.downloadCatalog (RemoteCatalogArchetypeDataSource.java:119)
at org.apache.maven.archetype.source.RemoteCatalogArchetypeDataSource.getArchetypeCatalog (RemoteCatalogArchetypeDataSource.java:87)
at org.apache.maven.archetype.DefaultArchetypeManager.getRemoteCatalog (DefaultArchetypeManager.java:216)
at org.apache.maven.archetype.ui.generation.DefaultArchetypeSelector.getArchetypesByCatalog (DefaultArchetypeSelector.java:218)
at org.apache.maven.archetype.ui.generation.DefaultArchetypeSelector.selectArchetype (DefaultArchetypeSelector.java:71)
at org.apache.maven.archetype.mojos.CreateProjectFromArchetypeMojo.execute (CreateProjectFromArchetypeMojo.java:181)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:208)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:154)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:146)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128)
at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:305)
at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
at org.apache.maven.cli.MavenCli.execute (MavenCli.java:954)
at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:288)
at org.apache.maven.cli.MavenCli.main (MavenCli.java:192)
at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke (Method.java:498)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:289)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:229)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:415)
at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:356)
Caused by: org.apache.maven.wagon.providers.http.httpclient.client.ClientProtocolException: URI does not specify a valid host name: http:/cometd.org
at org.apache.maven.wagon.providers.http.httpclient.impl.client.CloseableHttpClient.determineTarget (CloseableHttpClient.java:95)
at org.apache.maven.wagon.providers.http.httpclient.impl.client.CloseableHttpClient.execute (CloseableHttpClient.java:83)
at org.apache.maven.wagon.providers.http.wagon.shared.AbstractHttpClientWagon.execute (AbstractHttpClientWagon.java:825)
at org.apache.maven.wagon.providers.http.wagon.shared.AbstractHttpClientWagon.fillInputData (AbstractHttpClientWagon.java:986)
at org.apache.maven.wagon.providers.http.wagon.shared.AbstractHttpClientWagon.fillInputData (AbstractHttpClientWagon.java:963)
at org.apache.maven.wagon.StreamWagon.getInputStream (StreamWagon.java:126)
at org.apache.maven.wagon.StreamWagon.getIfNewer (StreamWagon.java:88)
at org.apache.maven.wagon.StreamWagon.get (StreamWagon.java:61)
at org.apache.maven.archetype.source.RemoteCatalogArchetypeDataSource.downloadCatalog (RemoteCatalogArchetypeDataSource.java:119)
at org.apache.maven.archetype.source.RemoteCatalogArchetypeDataSource.getArchetypeCatalog (RemoteCatalogArchetypeDataSource.java:87)
at org.apache.maven.archetype.DefaultArchetypeManager.getRemoteCatalog (DefaultArchetypeManager.java:216)
at org.apache.maven.archetype.ui.generation.DefaultArchetypeSelector.getArchetypesByCatalog (DefaultArchetypeSelector.java:218)
at org.apache.maven.archetype.ui.generation.DefaultArchetypeSelector.selectArchetype (DefaultArchetypeSelector.java:71)
at org.apache.maven.archetype.mojos.CreateProjectFromArchetypeMojo.execute (CreateProjectFromArchetypeMojo.java:181)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:208)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:154)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:146)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128)
at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:305)
at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
at org.apache.maven.cli.MavenCli.execute (MavenCli.java:954)
at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:288)
at org.apache.maven.cli.MavenCli.main (MavenCli.java:192)
at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke (Method.java:498)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:289)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:229)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:415)
at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:356)
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)

What am I missing in my configuration? What is the correct address of the CometD archetype catalog?

Thanks a lot


Solution

  • In general, CometD recommends to deploy in Jetty and to use the latest release (at this time 4.0.4).

    Your web.xml has incorrect values for the transports init-param.

    JettyWebSocketTransport is a CometD transport that uses Jetty-specific API in its implementation; as such, it only works when your application is deployed in Jetty.

    I recommend that you remove the transports init-param and let CometD pick the proper defaults, which will work in both Jetty and Tomcat.

    For the record, the default WebSocket transport is org.cometd.websocket.server.WebSocketTransport which is based on the standard JSR 356 APIs that are implemented by both Jetty and Tomcat.

    Your client is correct, although there also you may want to use the standard WebSocket transport, org.cometd.websocket.client.WebSocketTransport.

    I tried the command to generate a project from the archetypes and works fine for me. Perhaps you have a hint in the error: URI does not specify a valid host name: http:/cometd.org - see how there is only 1 slash, not the required 2 slashes.

    In summary: