Jetty, websocket, java.lang.RuntimeException: Cann

2019-08-01 17:17发布

问题:

I try to get http session in Endpoint. I followed this recomendations https://stackoverflow.com/a/17994303 . Thats why I did:

public class MyConfigurator extends ServerEndpointConfig.Configurator
{
    @Override
    public void modifyHandshake(ServerEndpointConfig config, 
                                HandshakeRequest request, 
                                HandshakeResponse response)
    {
        HttpSession httpSession = (HttpSession)request.getHttpSession();
        config.getUserProperties().put(HttpSession.class.getName(),httpSession);
    }
}

and

@ServerEndpoint(value = "/foo", configurator = MyConfigurator.class)
public class MyEndpoint {

    private Session wsSession;

    private HttpSession httpSession;


    @OnOpen
    public void open(final Session session,EndpointConfig config) {
        this.wsSession=session;
        this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
    }
}

And this is what I get

java.lang.RuntimeException: Cannot load platform configurator
    at javax.websocket.server.ServerEndpointConfig$Configurator.fetchContainerDefaultConfigurator(ServerEndpointConfig.java:123)
    at javax.websocket.server.ServerEndpointConfig$Configurator.getContainerDefaultConfigurator(ServerEndpointConfig.java:128)
    at javax.websocket.server.ServerEndpointConfig$Configurator.checkOrigin(ServerEndpointConfig.java:192)
    at org.eclipse.jetty.websocket.jsr356.server.JsrCreator.createWebSocket(JsrCreator.java:88)
    at org.eclipse.jetty.websocket.server.WebSocketServerFactory.acceptWebSocket(WebSocketServerFactory.java:187)
    at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:207)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1676)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:581)
    at org.ops4j.pax.web.service.jetty.internal.HttpServiceServletHandler.doHandle(HttpServiceServletHandler.java:70)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1180)
    at org.ops4j.pax.web.service.jetty.internal.HttpServiceContext.doHandle(HttpServiceContext.java:276)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1112)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
    at org.ops4j.pax.web.service.jetty.internal.JettyServerHandlerCollection.handle(JettyServerHandlerCollection.java:80)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
    at org.eclipse.jetty.server.Server.handle(Server.java:524)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:319)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:253)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:273)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
    at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)
    at java.lang.Thread.run(Thread.java:745)

I use osgi, jetty 9.3.11 and pax-web 6.0.0.

回答1:

The problem is that the javax.websocket-api jar is not set up for using the ServiceLoader mechanism in osgi so it can find custom Configurator instances.

In order for that to work in osgi, the manifest in the javax.websocket-api jar would need to have these lines:

Require-Capability: osgi.serviceloader;filter:="(osgi.serviceloader=javax.websocket.server.ServerEndpointConfig.Configurator)";resolution:=optional;cardin
 ality:=multiple,osgi.extender;filter:="(osgi.extender=osgi.serviceloa
 der.processor)"

So as it does NOT have those lines, you are going to need to add another bundle with a manifest with those lines in it and that declares itself as a fragment for the javax.websocket-api bundle.

If you're using maven, the lines you'd need in a pom are something like:

      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <configuration>
            <instructions>
              <Bundle-SymbolicName>${bundle-symbolic-name};singleton:=true</Bundle-SymbolicName>
              <Bundle-Name>OSGi Websocket API Fragment</Bundle-Name>
              <Fragment-Host>javax.websocket-api</Fragment-Host>
            <Require-Capability>osgi.serviceloader; filter:="(osgi.serviceloader=)javax.websocket.server.ServerEndpointConfig.Configurator";resolution:=optional;cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)"</Require-Capability>
            </instructions>
        </configuration>
      </plugin>


回答2:

You can install the mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.javax-websocket-api/1.1_1 bundle which is a repackage of the original javax.websocket-api but with proper OSGi descriptors. More on this here: http://mavi.logdown.com/posts/7813065-deploying-vaadin-app-on-karaf



回答3:

This is what worked for creating my fragment fix:

        <plugin>
            <groupId>org.apache.felix</groupId>
            <artifactId>maven-bundle-plugin</artifactId>
            <extensions>true</extensions>
            <configuration>
                <instructions>
                    <Bundle-SymbolicName>${project.groupId}.${project.artifactId};singleton:=true</Bundle-SymbolicName>
                    <Bundle-Name>Websocket API OSGi Fix</Bundle-Name>
                    <Fragment-Host>javax.websocket-api</Fragment-Host>
                    <Require-Capability>
                        osgi.serviceloader;osgi.serviceloader="javax.websocket.server.ServerEndpointConfig$Configurator"
                    </Require-Capability>
                </instructions>
            </configuration>
        </plugin>


回答4:

This is not an answer, but we thought it might help other users who run into the same issue that we did: we had the same exact problem as described by the OP but the "accepted answer" did not fix it. That is, we added precisely the described entry to the Manifest in the specified jar, and we continued to get the same exception (identical to the one listed by the OP).

We managed to solve the issue as follows. In our Dropwizard initialize() method, we had code like:

@Override
public void initialize(Bootstrap<AppConfig> bootstrap) {
   ...
   bootstrap.addBundle(new WebsocketBundle(MyWebsocketHandler.class));
   ...
}

After tracing through the Dropwizard code, we discovered that the WebsocketBundle constructor we were invoking was assigning null to the default configurator, despite the fact that the @ServerEndpoint annotation on the MyWebsocketHandler class specified a Configurator class, as follows:

@ServerEndpoint(value="/myWebsocketPath",configurator=CustomEndpointConfigurator.class)

We changed the constructor call in initialize() to instead be

bootstrap.addBundle(new WebsocketBundle(new CustomEndpointConfigurator(), TeamWebsocketHandler.class));

and poof, the exception went away. (It DID result in a long succession of new "NoClassDefFound" exceptions, but those are easy to find. After installing the missing libraries containing all the missing classes, the application websockets began functioning as expected.)

It is entirely possible that we have completely misinterpreted something about how Dropwizard works (or is supposed to work); those of us on this project are all serious novices when it comes to this area, and we would welcome any explanation of why what we did was "wrong" or "doesn't make sense". But we thought it might help others to discover something that worked for us when the "accepted answer" didn't.