Websockets in DropWizard

I’ve been playing with WebSockets for a while, and I wanted to use DropWizard as a WebSocket server host.

DropWizard is en extremely developer friendly Web framework based on Jersey, Java and Jetty. It is really powerful and easy to get to. The intention of DropWizard is to make micro-services with a REST based interface. Of course you can do anything you like, but out of the box, that is what you get.

The problem is that WebSockets doesn’t fit the bill of the REST architecture, since state is very important for WebSockets. And there was no obvious way to bent DropWizard into serving WebSockets. Through much debugging and googling, I eventually found My Way to do it, and here it comes.

Your first action would be to include the Atmosphere dependencies into your pom.xml:

<properties>
    <dropwizard.version>0.7.1</dropwizard.version>
    <atmosphere-version>2.2.0</atmosphere-version>
</properties>
...
<dependency>
    <groupId>org.atmosphere</groupId>
    <artifactId>atmosphere-runtime</artifactId>
    <version>${atmosphere-version}</version>
</dependency>
<dependency>
    <groupId>org.atmosphere</groupId>
    <artifactId>atmosphere-annotations</artifactId>
    <version>${atmosphere-version}</version>
</dependency>
...

DropWizard’s starting point is a Application. Here you register your resources and bind everything together. The first thing to do is to register a new Servlet for handling WebSocket requests. I’ve chosen Atmosphere, so I’ll register the Atmosphere servlet:

Somewhere in the run() method, add the lines:

        AtmosphereServlet servlet = new AtmosphereServlet();
        servlet.framework().addInitParameter("com.sun.jersey.config.property.packages", "dk.cooldev.chatroom.resources.websocket");
        servlet.framework().addInitParameter(ApplicationConfig.WEBSOCKET_CONTENT_TYPE, "application/json");
        servlet.framework().addInitParameter(ApplicationConfig.WEBSOCKET_SUPPORT, "true");

        ServletRegistration.Dynamic servletHolder = environment.servlets().addServlet("Chat", servlet);
        servletHolder.addMapping("/chat/*");

This instructs DropWizard to make room for a servlet that gets the request if the url pattern matches /chat/*. I also instructs the Atmosphere framework to scan the

dk.cooldev.chatroom.resources.websocket

package to find annotated resources for handling the websocket requests. So we better put one in:

@Path("/")
@AtmosphereHandlerService(path = "/chat",
        broadcasterCache = UUIDBroadcasterCache.class,
        interceptors = {AtmosphereResourceLifecycleInterceptor.class,
                BroadcastOnPostAtmosphereInterceptor.class,
                TrackMessageSizeInterceptor.class,
                HeartbeatInterceptor.class
        })
public class ChatService extends OnMessage<String> {
    private final ObjectMapper mapper = new ObjectMapper();

    @Override
    public void onMessage(AtmosphereResponse response, String message) throws IOException {
        response.write(mapper.writeValueAsString(mapper.readValue(message, User.class)));
    }
}

That’s it, actually.

The problem I ran into with this solution was that my client code downgraded the connection type to long-polling instead of WebSockets, which I found strange because DropWizard uses Jetty 9, which supports WebSockets.

The DropWizard logs also kept claiming that WebSockets were not enabled:

ERROR [2014-08-02 06:40:38,544] org.atmosphere.cpr.DefaultAsyncSupportResolver: Failed to create comet support class: class org.atmosphere.container.JettyServlet30AsyncSupportWithWebSocket, error: null
ERROR [2014-08-02 06:40:38,544] org.atmosphere.cpr.DefaultAsyncSupportResolver: Switching to BlockingIO

So there was something missing on the classpath – but what? After a debugging session I found that it tried to make use of class org.eclipse.jetty.websocket.server.WebSocketServerFactory. This class was not included in the default DropWizard framework dependencies.

Therefore, in you pom.xml, add the following dependency:

<dependency>
    <groupId>org.eclipse.jetty.websocket</groupId>
    <artifactId>websocket-server</artifactId>
    <version>9.0.7.v20131107</version>
</dependency>

Notice the precise version above. It matches what the version of DropWizard uses internally for its Jetty dependencies.

Now it works. The client code informs me that WebSockets are enabled. This should get your started with WebSockets on DropWizard. Happy hacking!

6 thoughts on “Websockets in DropWizard

  1. Hi, thanks for the tutorial, very helpful.
    I need to add more fields to the ChatService class and put them into the constructor so when I receive a message I can access those fields. But when I do that atmosphere tries to find a constructor with no argument and it fails at scanning stage. Do you know how to fix this?

  2. Thanks for sharing! How did you find the correct, precise version of the websocket-server dependency? We’re still on Dropwizard v0.6.2.

    • Well, I think I figured it out. Looking at the dependencies, it looks like it should be 8.1.10.v20130312 but maven isn’t finding that version for me. I’ll keep digging.

Leave a comment