How to develop Tomcat-based WebSocket application on Heroku
Overview
When we want to develop WebSocket application based on Java, Tomcat is a good fit because Tomcat 7 and onwards support WebSocket in default. Nevertheless, Tomcat deployed on Heroku Cloud PaaS needs slight tweaks.
There are two existing solutions:
- Embedded Tomcat: https://devcenter.heroku.com/articles/create-a-java-web-application-using-embedded-tomcat
- Webapp Runner: https://devcenter.heroku.com/articles/java-webapp-runner
Unfortunately, those two solutions don't work for WebSocket applications that work under standard tomcat.
The Reason why exisiting solutions don't work
The critical part makes WebSocket up and running in Tomcat is two jars under lib directory -- websocket-api.jar and tomcat7-websocket.jar, they are the implementation of JSR 356, Java API for WebSocket, the link tells the details, http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html.
If we can find those two or stuff like that with regard to dependencies required by above solutions, they are supposed most likely to work.
If we can find those two or stuff like that with regard to dependencies required by above solutions, they are supposed most likely to work.
Let's check embedded tomcat solution at first, run following command against dependencies:
The result shows noting is related to JSR 356.
Let's run another command to see the contents inside the jars:
Again, the result shows nothing is related to JSR 356.
Let's check Webapp Runner solution secondly, there is only one dependency, which obviously are not directly related to WebSocket. Therefore, run following command against dependencies to check contents inside:
Although the result shows something like WebSocket, they are not relevant to the JSR 356. Those are not compatible to the standard Tomcat WebSocket implementation.
find . -name *.jar | grep websocket
The result shows noting is related to JSR 356.
Let's run another command to see the contents inside the jars:
find . -name *.jar | xargs -L 1 jar tvf | grep websocket
Again, the result shows nothing is related to JSR 356.
Let's check Webapp Runner solution secondly, there is only one dependency, which obviously are not directly related to WebSocket. Therefore, run following command against dependencies to check contents inside:
jar tvf webapp-runner.jar | grep websocket
0 Wed Aug 22 14:35:46 NZST 2012 org/jboss/netty/handler/codec/http/websocket/
0 Wed Aug 22 14:35:46 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/
2730 Wed Aug 22 14:35:10 NZST 2012 org/jboss/netty/handler/codec/http/websocket/DefaultWebSocketFrame.class
897 Wed Aug 22 14:35:10 NZST 2012 org/jboss/netty/handler/codec/http/websocket/WebSocketFrame.class
3158 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocket/WebSocketFrameDecoder.class
2651 Wed Aug 22 14:35:10 NZST 2012 org/jboss/netty/handler/codec/http/websocket/WebSocketFrameEncoder.class
1504 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/BinaryWebSocketFrame.class
2690 Wed Aug 22 14:35:08 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/CloseWebSocketFrame.class
2718 Wed Aug 22 14:35:10 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/ContinuationWebSocketFrame.class
1507 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/PingWebSocketFrame.class
1498 Wed Aug 22 14:35:10 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/PongWebSocketFrame.class
2414 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/TextWebSocketFrame.class
493 Wed Aug 22 14:35:10 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/UTF8Exception.class
3619 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/UTF8Output.class
3725 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.class
2774 Wed Aug 22 14:35:10 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocket00FrameEncoder.class
1136 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder$1.class
1509 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder$State.class
10107 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.class
4684 Wed Aug 22 14:35:10 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocket08FrameEncoder.class
657 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocket13FrameDecoder.class
473 Wed Aug 22 14:35:10 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocket13FrameEncoder.class
2810 Wed Aug 22 14:35:08 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.class
8647 Wed Aug 22 14:35:08 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.class
7844 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.class
7841 Wed Aug 22 14:35:10 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.class
2980 Wed Aug 22 14:35:10 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.class
1117 Wed Aug 22 14:35:08 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketFrame.class
1410 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketFrameType.class
708 Wed Aug 22 14:35:08 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketHandshakeException.class
1160 Wed Aug 22 14:35:08 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker$1.class
3420 Wed Aug 22 14:35:08 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.class
6518 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.class
6352 Wed Aug 22 14:35:08 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.class
6354 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.class
3144 Wed Aug 22 14:35:12 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.class
2011 Wed Aug 22 14:35:10 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketUtil.class
1794 Wed Aug 22 14:35:08 NZST 2012 org/jboss/netty/handler/codec/http/websocketx/WebSocketVersion.class
Although the result shows something like WebSocket, they are not relevant to the JSR 356. Those are not compatible to the standard Tomcat WebSocket implementation.
The workaround solution
After having a close look at the embedded solution, we find embedded tomcat comprises of several sub modules. Let's check whether WebSocket is one of sub modules, if so, the rescue comes true.
The embedded WebSocket module does exist:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<version>${tomcat.version}</version>
</dependency>
After adding this dependency into the solution one, the WebSocket application working under standard tomcat also works by Embedded Tomcat.
A GitHub Example
You can find this github repository: https://github.com/Bo-Ye/heroku-tomcat-websocket to deploy onto Heroku. This project is a complete example to demonstrate this workable solution. Run following commands to deploy this project onto Heroku. $ heroku create heroku-tomcat-websocket
Creating heroku-tomcat-websocket... done, stack is cedar-14
https://heroku-tomcat-websocket.herokuapp.com/ | https://git.heroku.com/heroku-tomcat-websocket.git
Git remote heroku added
$ git push heroku master
Counting objects: 60, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (41/41), done.
Writing objects: 100% (60/60), 6.19 KiB | 0 bytes/s, done.
Total 60 (delta 11), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Java app detected
remote: -----> Installing OpenJDK 1.8... done
remote: -----> Installing Maven 3.3.9... done
remote: -----> Executing: mvn -B -DskipTests clean dependency:list install
remote: [INFO] Scanning for projects...
$ heroku open
Opening heroku-tomcat-websocket... done
This example features the browser is getting random messages per second form the backend through Get Messages button and stops getting messages through Stop Messages button.
Tomcat-based WebSocket on Heroku |
Happy coding!
You are right that tomcat-based application will not work. I wan't to propose solution using Embedded Jetty:
ReplyDeletehttp://privateblog.info/heroku-com-kak-poluchit-realizaciyu-websockets-na-java-jsr-365-podxod/