I'm using Dropwizard for a REST server and dropwizard-websocket-jee7-bundle to enable websockets.
For the websocket server I used this example.
Testing the websocket server standalone works fine, but in combination with Dropwizard, when a client tries to connect (to ws://localhost:port/actions) it gets a 500 Internal Server Error (Error log below).
I'm guessing there is some bad or missing configuration, but I can't figure our where.
ServerExample:
package com.example;
import static org.eclipse.jetty.servlets.CrossOriginFilter.ALLOWED_HEADERS_PARAM;
import static org.eclipse.jetty.servlets.CrossOriginFilter.ALLOWED_METHODS_PARAM;
import static org.eclipse.jetty.servlets.CrossOriginFilter.ALLOWED_ORIGINS_PARAM;
import static org.eclipse.jetty.servlets.CrossOriginFilter.ALLOW_CREDENTIALS_PARAM;
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import org.eclipse.jetty.servlets.CrossOriginFilter;
import com.example.health.SearchHealthCheck;
import com.example.resources.TestFind;
import be.tomcools.dropwizard.websocket.WebsocketBundle;
import io.dropwizard.Application;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import com.example.websocket.DeviceWebSocketServer;
public class ServerExample extends Application<ServerExampleConfiguration> {
private WebsocketBundle websocket = new WebsocketBundle();
public static void main(String[] args) throws Exception {
new ServerExample().run(args);
}
@Override
public String getName() {
return "com.example";
}
@Override
public void initialize(Bootstrap<ServerExampleConfiguration> bootstrap) {
super.initialize(bootstrap);
bootstrap.addBundle(websocket);
}
@Override
public void run(ServerExampleConfiguration configuration, Environment environment) throws Exception {
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
FilterRegistration.Dynamic filter = environment.servlets().addFilter("CORSFilter", CrossOriginFilter.class);
filter.setInitParameter(ALLOWED_METHODS_PARAM, "OPTIONS,POST,GET");
filter.setInitParameter(ALLOWED_ORIGINS_PARAM, "*");
filter.setInitParameter(ALLOWED_HEADERS_PARAM, "Origin,Content-Type,Accept,X-Requested-With");
filter.setInitParameter(ALLOW_CREDENTIALS_PARAM, "true");
filter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
environment.jersey().register(new TestFind());
environment.healthChecks().register("search", new SearchHealthCheck());
//Annotated endpoint
websocket.addEndpoint(DeviceWebSocketServer.class);
}
}
DeviceWebSocketServer:
package com.example.websocket;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.io.StringReader;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import com.example.model.Device;
import java.util.logging.Level;
import java.util.logging.Logger;
@ApplicationScoped
@ServerEndpoint("/actions")
public class DeviceWebSocketServer {
@Inject
private DeviceSessionHandler sessionHandler;
@OnOpen
public void open(Session session) {
sessionHandler.addSession(session);
}
@OnClose
public void close(Session session) {
sessionHandler.removeSession(session);
}
@OnError
public void onError(Throwable error) {
Logger.getLogger(DeviceWebSocketServer.class.getName()).log(Level.SEVERE, null, error);
}
@OnMessage
public void handleMessage(String message, Session session) {
try (JsonReader reader = Json.createReader(new StringReader(message))) {
JsonObject jsonMessage = reader.readObject();
if ("add".equals(jsonMessage.getString("action"))) {
Device device = new Device();
device.setName(jsonMessage.getString("name"));
device.setDescription(jsonMessage.getString("description"));
device.setType(jsonMessage.getString("type"));
device.setStatus("Off");
sessionHandler.addDevice(device);
}
if ("remove".equals(jsonMessage.getString("action"))) {
int id = (int) jsonMessage.getInt("id");
sessionHandler.removeDevice(id);
}
if ("toggle".equals(jsonMessage.getString("action"))) {
int id = (int) jsonMessage.getInt("id");
sessionHandler.toggleDevice(id);
}
}
}
}
DeviceSessionHandler:
package com.example.websocket;
import javax.enterprise.context.ApplicationScoped;
import javax.json.JsonObject;
import javax.json.spi.JsonProvider;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.websocket.Session;
import com.example.model.Device;
@ApplicationScoped
public class DeviceSessionHandler {
private int deviceId = 0;
private final Set<Session> sessions = new HashSet<>();
private final Set<Device> devices = new HashSet<>();
public void addSession(Session session) {
sessions.add(session);
for (Device device : devices) {
JsonObject addMessage = createAddMessage(device);
sendToSession(session, addMessage);
}
}
public void removeSession(Session session) {
sessions.remove(session);
}
public List<Device> getDevices() {
return new ArrayList<>(devices);
}
public void addDevice(Device device) {
device.setId(deviceId);
devices.add(device);
deviceId++;
JsonObject addMessage = createAddMessage(device);
sendToAllConnectedSessions(addMessage);
}
public void removeDevice(int id) {
Device device = getDeviceById(id);
if (device != null) {
devices.remove(device);
JsonProvider provider = JsonProvider.provider();
JsonObject removeMessage = provider.createObjectBuilder()
.add("action", "remove")
.add("id", id)
.build();
sendToAllConnectedSessions(removeMessage);
}
}
public void toggleDevice(int id) {
JsonProvider provider = JsonProvider.provider();
Device device = getDeviceById(id);
if (device != null) {
if ("On".equals(device.getStatus())) {
device.setStatus("Off");
} else {
device.setStatus("On");
}
JsonObject updateDevMessage = provider.createObjectBuilder()
.add("action", "toggle")
.add("id", device.getId())
.add("status", device.getStatus())
.build();
sendToAllConnectedSessions(updateDevMessage);
}
}
private Device getDeviceById(int id) {
for (Device device : devices) {
if (device.getId() == id) {
return device;
}
}
return null;
}
private JsonObject createAddMessage(Device device) {
JsonProvider provider = JsonProvider.provider();
JsonObject addMessage = provider.createObjectBuilder()
.add("action", "add")
.add("id", device.getId())
.add("name", device.getName())
.add("type", device.getType())
.add("status", device.getStatus())
.add("description", device.getDescription())
.build();
return addMessage;
}
private void sendToAllConnectedSessions(JsonObject message) {
for (Session session : sessions) {
sendToSession(session, message);
}
}
private void sendToSession(Session session, JsonObject message) {
try {
session.getBasicRemote().sendText(message.toString());
} catch (IOException ex) {
sessions.remove(session);
Logger.getLogger(DeviceSessionHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Dependencies:
<dependencies>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.7</version>
</dependency>
<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-arq</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>1.0.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.8</version>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
<version>1.1</version>
</dependency><dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>be.tomcools</groupId>
<artifactId>dropwizard-websocket-jee7-bundle</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
Error Log:
WARN [2017-08-31 16:55:46,863] org.eclipse.jetty.servlet.ServletHandler: Error for /actions
! java.lang.NoSuchMethodError: org.eclipse.jetty.io.AbstractConnection.<init>(Lorg/eclipse/jetty/io/EndPoint;Ljava/util/concurrent/Executor;Z)V
! at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.<init>(AbstractWebSocketConnection.java:225)
! at org.eclipse.jetty.websocket.server.WebSocketServerConnection.<init>(WebSocketServerConnection.java:41)
! at org.eclipse.jetty.websocket.server.WebSocketServerFactory.upgrade(WebSocketServerFactory.java:520)
! at org.eclipse.jetty.websocket.server.WebSocketServerFactory.acceptWebSocket(WebSocketServerFactory.java:186)
! at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:206)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1676)
! at io.dropwizard.servlets.ThreadNameFilter.doFilter(ThreadNameFilter.java:34)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1676)
! at io.dropwizard.jersey.filter.AllowedMethodsFilter.handle(AllowedMethodsFilter.java:50)
! at io.dropwizard.jersey.filter.AllowedMethodsFilter.doFilter(AllowedMethodsFilter.java:44)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1676)
! at org.eclipse.jetty.servlets.CrossOriginFilter.handle(CrossOriginFilter.java:308)
! at org.eclipse.jetty.servlets.CrossOriginFilter.doFilter(CrossOriginFilter.java:262)
! at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1676)
! at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:581)
! at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1174)
! at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511)
! at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1106)
! at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
! at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
! at com.codahale.metrics.jetty9.InstrumentedHandler.handle(InstrumentedHandler.java:240)
! at io.dropwizard.jetty.RoutingHandler.handle(RoutingHandler.java:51)
! at org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:459)
! at io.dropwizard.jetty.BiDiGzipHandler.handle(BiDiGzipHandler.java:68)
! at org.eclipse.jetty.server.handler.RequestLogHandler.handle(RequestLogHandler.java:56)
! at org.eclipse.jetty.server.handler.StatisticsHandler.handle(StatisticsHandler.java:169)
! 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)
0:0:0:0:0:0:0:1 - - [31/ago/2017:16:55:46 +0000] "GET /actions HTTP/1.1" 500 245 "-" "-" 47
Edited: Added dependencies list.
Downgraded the dropwizard version I was using to the last reported version in the bundle (0.9.1) and this error is gone (although i have a null pointer exception now. will open a new question about that error).