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.