In between, there has been an official implementation of Pebble for Vertx. You should use this dependency in favor of my implementation.
It’s been a long time since I wrote something in here. That is mainly because I moved to a project where no Java was involved and I had to code PHP for quite some time. While I did this, I came closer to web development and the bootstrap-library from twitter. For a new project I decided to switch to Java again, because we had realtime requirements and – to be honest – because I wanted to code in Java again. A lot has happened in between though: Java 8 came out and the whole micro-services train started to move into direction from theoretical discussions to rock-solid technologies which are used in production. One of which is vertx which was released in the third version.
In this blogpost I want to show you how you can write a simple chat application using the technologies mentioned above:
- Vertx: „A polyglot tool-kit to build reactive applications on the JVM”
- Bootstrap: „The most popular HTML, CSS, and JS framework for developing responsive, mobile first projects on the web“
- Pebble: „A lightweight but rock solid Java templating engine”
The WebSocket Server
While I’ve been working on the PHP project, I realized that I don’t have to care about writing web servers. You just write your code and let application servers like apache or nginx do the rest. But we are back in the Java-World. And in the Java-World you have to take care of that. This post is not a deep-dive into every aspect of vertx and I will just explain the parts that are necessary for this post to be understood. If you’re hooked, I recommend you reading their docs. Please note that you don’t have to copy the code snippets one by one, because I’ve setup a github repo for this purpose.
Line 4: First of all, we need a
Router. It is used to route the request coming from the vertx-Httpserver. We define a route and handler which is responsible for handling that route so whenever someone is calling „/ws/websocket/„ the handler will be called. The asterisk in this case means that the request could also be „/ws/websocket/asdf“ and would still be handled by that handler.
Line 5: This is how to upgrade a regular request to a WebSockets in vertx (for the sake of completeness: there are also other ways of handling WebSockets in vertx, but I liked this the most).
Line 7: Here we added a handler that is called whenever a message is received on the WebSocket. We expect messages to be binary encoded JSON. For every message we add a senderId-field with a unique identifier for each client to the message and send it to the event bus. The event bus is used to the send messages between several vertx instances – either locally or even clusterwide. But it also works only for one vertx instances like in our case. Every message needs to have an address which is in our case „chat.broadcast“. One thing worth mentioning: there is also a send-method on the eventbus which implies the call of ONE handler, whereas publish will signal all registered handlers.
Line 13: This is how the client will receive messages. We register a consumer on the eventbus that is notified whenever a message is published. We evaluate the notification and check for the senderId-field. If we are not the sender, the message is send to our client.
Line 21: If something unexpected happens, we want to log it.
Line 22: When the client is closed, we have to cleanup everything.
Line 27: Ramp up the HTTP server with the defined route.
That is all logic needed for handling the client-server communication.
The web server
For convenience reasons, I decided to add the client into the same application. This basically means that we need to have a web server that delivers the HTML-document which has a java-script-client embedded.
StaticHandler which is part of the Vertx library. We need to define a base-path in which our files are stored. In our case it is “webroot/resources“ which I’ve created under „src/main/resources/„. (The
Router is the same that we used for the WebSocket-server. This is important, because if we would create a
Router in each
Verticle some requests wouldn’t be served. This is because the
HttpServer would call the
Routers in a round-robin-fashion and therefore not all routes we defined would be known.)
Line 5: Our app will have a fancy favicon, this is how you deliver it.
Line 6: In this example we’re using a templating engine to render our HTML pages. Vertx comes with an out-of-the-box-support of four template engines but I wanted to give pebble a try. The main reason for using Pebble is that I like the templating syntax and it gives a nice performance (https://github.com/mbosecke/template-benchmark) too. Therefore I created my own PebbleTemplate-Engine and added it to the TemplateHandler. The templates are located under „webroot/templates” in this example.
Line 9: Route every request that has not matched by any other route to our chat_client.html.
Line 10: Ramping up the HTTP server. But wait! Didn’t we use the same port for the WebSocket-server? Yes we did, but vertx is smart enough to combine the routes and handle everything accordingly.
We create the server using the good old main-method and instantiate a Vertx-instance as shown. Vertx has the concept of
Verticles that provide„a simple, scalable, actor-like deployment and concurrency model“. One benefit is that you can write code as if it was single threaded, because code is always called from the same eventloop. The whole example would totally have worked without
Verticles, but creating a Router and everything directly in the main-method. Using
Verticles however encourages you to separate your concerns and can be used as an entry-point for developing microservices. Another thing worth mentioning is, that
Verticles are deployed asynchronously and therefore the
Main-Thread will not block until they are deployed.
This is the skeleton from which every page will inherit and is basically the start-template provided by bootstrap, however divided into several parts. The
include-keyword includes the content of the given source into this document like you would expect (one has to prepend a dot followed by a slash to the source-name in order to make the loading work (at least on my mac)). The HTML itself is pretty basic but I’ll explain the used element classes that come with bootstrap:
container-fluiddescribes a container spanning over the whole document
rowsare used to define a horizontal group of columns
col-md-offset-1describes that we’ll have 1 column padding on each side without content. The
col-md-10describes a column that has a size of 10/12 of the width of the enclosing row-div since rows are divided in 12
md-columns. If we would add two divs with each having the class
col-md-5, we would end up having two columns in one row (plus the two offset-columns). Because bootstrap is mobile first, this is responsive and automatically scales well on all kinds of devices. We could also define the classes not using
col-s-xto have different layouts for devices with extra-small (phones) or small (tablets) devices. For further reading I recommend the bootstrap-docs (http://getbootstrap.com/css/#grid).
- the last div has the
starter-templateclass which basically moves the content below the navigation header.
Now we are coming to another interesting part of the template engine. We define a
block called ‚content‘ which can be overridden by any page that is extending this document. (In our example we only have one site – the chat application – which makes the usage of templates a bit obsolete, but that is another story ;)). We could also add some default values but in this case it is not necessary. The key takeaway from this is that you can write all the boilerplate code in one file and when writing a new pages you just have to add the parts that are new.
By using the
extends keyword we tell Pebble to inherit from that document. We’re overwriting the content-
binaryType accordingly. The rest of the code is not very hard to understand and should be self-explainority.
I hosted the complete project on GitHub. The project requires maven and Java 8 to be built properly. After you cloned the project, run
Main.java in the IDE of your choice, open two tabs in your browser, point to localhost:8080, select a username and start chatting! As you can see, messages are instantly pushed to the other clients – the benefit of WebSockets. I left some space for modifications though: right now you can only broadcast messages. But what if you want to send a private message to just one recipient? Maybe you want to personalize your account by uploading avatars or securing them with a password on a separate page/template? If I find the time I will cover this in one of my next blog-posts, but until then: happy coding!