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:
  1. Embedded Tomcathttps://devcenter.heroku.com/articles/create-a-java-web-application-using-embedded-tomcat
  2. Webapp Runnerhttps://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.

Let's check embedded tomcat solution at first, run following command against dependencies:

 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!

Comments

  1. You are right that tomcat-based application will not work. I wan't to propose solution using Embedded Jetty:
    http://privateblog.info/heroku-com-kak-poluchit-realizaciyu-websockets-na-java-jsr-365-podxod/

    ReplyDelete

Post a Comment

Popular posts from this blog

A trick for connecting spring security oauth2 and wso2 api manager

How to use bitcoinj to implement multisig transactions