domingo, 23 de junho de 2013

Using WebSockets to implement Dashboards



Dashboards are extremely important tools mostly used for monitoring systems and environments. The main idea of these tools is to keep clients up-to-date with arriving or changing data on the server side. For that, the most popular technique used in web applications is based on ajax polling. With ajax, the client polls the server for data every time the content of the page requires an update.


Once the idea of monitoring is to show actual data information, there are several problems with current ajax technique. The first one is scalability. The number of requests made to the server can be extremely high if the frequency of polling is set to a small value. Not only the server but also the network can become saturated with all those requests. On the other hand, if we set a high value for the pooling frequency, some information may be lost or delayed. Another problem is that the response may not contain any data. So, in these cases, ajax polling overloads the server for nothing.


Fortunately, in December 2011, a new protocol called WebSocket was defined. As the spec says: “The goal of this technology is to provide a mechanism for browser-based applications that need two-way communication with servers that does not rely on opening multiple HTTP connections”. In other words, with Websocket protocol it is possible to send message frames from server to clients, once these clients have established an initial conversation, of course.


Analyzing such a capability, it becomes clear that implementations of browser-based dashboards, chats, games, kanbans, planning pokers tools, etc should be rewritten to use WebSocket protocol. Therefore, the main idea of this post is to show how to implement a very simple dashboard using websockets.


First of all, I would refer my article about how to create and embedded war with Jetty and Gradle. To make this post short and simple, I will assume you have already read that post or that you have a good knowledge about these subjects.


Second, to start our example we will be adding support for websockets in an embedded Jetty. So, you have to create your build file called ‘build.gradle’ as shown below. Note the lines in red, which are the responsible to include the expected support.


// Tells gradle it is a war project which will be imported into eclipse wtp
apply plugin: 'war'
apply plugin: 'eclipse-wtp'

// Define souce code compatibility
sourceCompatibility = 1.7
targetCompatibility = 1.7

// Use maven repository
repositories {
       mavenCentral()
}

// Fills out all dependencies which are necessary to start the embedded jetty into our war file
configurations {
       embeddedJetty
}
dependencies {
       embeddedJetty 'org.eclipse.jetty:jetty-servlet:+'
       embeddedJetty 'org.eclipse.jetty:jetty-webapp:+'
       embeddedJetty 'org.eclipse.jetty:jetty-jsp:+'
       embeddedJetty 'org.eclipse.jetty:jetty-annotations:+'

       // add support for jetty implementation of websockets
       embeddedJetty 'org.eclipse.jetty.websocket:websocket-server:+'

       compile 'org.glassfish:javax.json:+'
}

war.baseName = 'WebSockets'
war {
       // unzip and add all jetty dependencies into the root of our war file
       from {configurations.embeddedJetty.collect {
                       project.zipTree(it)
               }
         }
        // remove signature and unnecessary files
        exclude "META-INF/*.SF", "META-INF/*.RSA", "about.html", "about_files/**", "readme.txt", "plugin.properties", "jetty-dir.css"
        // include only the classes which will be used to start Embedded Jetty
        from "$buildDir/classes/main"
        exclude "com/myapp/"
        // tells the class to run when the generate war be executed using 'java -jar'
        manifest { attributes 'Main-Class': 'com.embedded.JettyStarter' }
}

// Once you will need some basic api (e.i. servlet api) for compilation, add embeddedJetty dependencies for compilation
sourceSets.main.compileClasspath += configurations.embeddedJetty

// the same for eclipse classpath, so you can use it to edit your java files
eclipse {
       classpath {
               plusConfigurations += configurations.embeddedJetty
       }
}


Unfortunately, Jetty 9 still does not support JSR 356 (JavaTM API for WebSocket). So we are going to use its current API in this example (which, by the way, is very similar to JSR 356). For that, initially we have to create a class for implementing our WebSocket as shown below.


import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
 
@WebSocket  
public class DashboardWebSocket  {
       @OnWebSocketClose
       public void onClose(final Session session, int x, String text) {
                 EventGenerator.unregistry(new EventObserver(session));
        }
       @OnWebSocketConnect
        public void onOpen(final Session session) {
                 EventGenerator.registry(new EventObserver(session));
        }  
}  


The most important parts of the above class are the onOpen(..) and onClose(..) methods, which will be executed at any WebSocket open and close connections. In our example, each time a user decides to navigate to this app we will be registering his session as an event observer. Doing that, it is possible to broadcast to all registered sessions any event which has happened in the server side. In the same way, whenever that user closes the browser, the connection is lost and his session will be unregistered from the event observer list.


Besides the WebSocket itself, using Jetty API, it is necessary to implement a WebSocketServlet to map and registry such WebSocket. In our example, each time an URI /dashboard is used; it will be redirected for this servlet.


import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;


@SuppressWarnings("serial")
@WebServlet(name = "Dashboard WebSocket Servlet", urlPatterns = { "/dashboard" })
public class DashboardServet  extends WebSocketServlet {
        @Override
        public void configure(WebSocketServletFactory factory) {
                 factory.register(DashboardWebSocket.class);
        }
}


Once these steps are done, it is necessary to use the JavaScript API to open the initial connection and make it wait for new message frames from the server. Note, in the below JS code, that we have used the protocol ‘ws’ instead of ‘http’. It is necessary because WebSocket is, actually, a different protocol.


<script>
var load = function() {
        var socket = new WebSocket("ws://localhost:8081/dashboard");
        socket.onmessage = function(event) {
                 // show the event.data in your graph, table or whatever
        }
}
</script>


In the above code, we presented a very simple implementation in which a WebSocket is opened and, on each server event, data is received as a parameter by the function onmessage(..). In below image it is possible to see a printscreen of the this app running. The points represent the event amount (for an example purpose we have used a random number from 0 to 100.000) and moment it have happened.



In short I would say WebSocket is pretty important technology for building modern web applications in which, in the near future, it will be largely used for enterprise software as well for general web applications. However, it is still complicate to assume websockets as an unique strategy to replace long polling features because there exists firewalls and proxies which does not understand/support ‘ws’ protocol yet. Besides that, in the Java world, there are still few application servers which provide support for JSR 356 standard. So, we still have to wait some more months to use the API specified in that standard (p.s.: Glassfish already provides such implementation).


If you would like to study and/or execute this example, you can download the full source code here. To start the dashbord, you have to build the war and then execute it with the following command line: ‘java -jar WebSockets.war’. This will start a Jetty server in port 8081, so you can access the http://localhost:8081/ url in the browser and see by yourself.


Moreover, if you would like to understand how websocket frames are sent to the browser, I would suggest you to open the ‘Chrome Developer tools’ and click in ‘Network’ tab. In there, you will be able to see that this example app will be making no ‘http’ requests. But only one ‘ws’ connection. Clicking in the ‘ws’ connection you will be able to see the message frames being sent back from the server as shown in the image below.



Thank you for reading!

domingo, 16 de junho de 2013

Embedded war using Jetty and Gradle


Recently I had the necessity to write an embedded war. At a first glance, I thought: “It is a pretty simple task, I’ve done it many times using Jetty”. Just some minutes later I remembered: “Wait a minute... I’ve used embedded Jetty servers into jar files, but how can I do the same using war files?”.

The answer of this question is a bit “tricky”. Actually, you have to create a war file which will have to play two different roles. The first one is responsible to start the embedded Jetty server and the second one is the web app itself. Take a look at the MyEmbeddedWar.war folder structure below to understand what I’m talking about:


  • MyEmbeddedWar.war
    • META-INF
      • MANIFEST.MF
        • containing: Main-Class: com.embedded.JettyStarter
    • Jetty classes (org.eclipse.jetty.*, org.apache.*, javax.*, etc)
    • My main class: com.embedded.JettyStarter
    • WEB-INF
      • classes
        • containing my app classes
      • web.xml
    • index.jsp


The blue files in the root of the war are those responsible to start the embedded Jetty server and the red ones represent the content of web app. Ok, once I’ve understood what need to be done, I’ve started to think how it could be done using gradle (for those which do not know what gradle is, I strongly recommend to take a look at http://www.gradle.org/). So, at the end of my experiments I’ve got the following:

First you need to create the file ‘build.gradle’ as shown below. I’ve added some comments on it to make this post as short and simple as possible.

// Tells gradle it is a war project which will be imported into eclipse wtp IDE
apply plugin: 'war'
apply plugin: 'eclipse-wtp'


// Define souce code compatibility
sourceCompatibility = 1.7
targetCompatibility = 1.7


// Use maven repository
repositories {
   mavenCentral()
}


// Fills out all dependencies which are necessary to start the embedded jetty into our war file
configurations {
embeddedJetty
}
dependencies {
embeddedJetty 'org.eclipse.jetty:jetty-servlet:+'
embeddedJetty 'org.eclipse.jetty:jetty-webapp:+'
embeddedJetty 'org.eclipse.jetty:jetty-jsp:+'
embeddedJetty 'org.eclipse.jetty:jetty-annotations:+'
}


war.baseName = 'MyExecutableWar'
war {
// unzip and add all jetty dependencies into the root of our war file
from {configurations.embeddedJetty.collect {
project.zipTree(it)
}
}
// remove signature and unnecessary files
exclude "META-INF/*.SF", "META-INF/*.RSA", "about.html", "about_files/**", "readme.txt", "plugin.properties", "jetty-dir.css"


// include in the root of the war only the classes which will be used to start the embedded Jetty
from "$buildDir/classes/main"
exclude "com/myapp/"
// sets the main class to run when the generate war be executed using 'java -jar'
manifest { attributes 'Main-Class': 'com.embedded.JettyStarter' }
}


// Once you will need some basic api (servlet api, for example) for compilation, you need to add embeddedJetty dependencies for compilation
sourceSets.main.compileClasspath += configurations.embeddedJetty


// you need to do the same for eclipse classpath, so you can use it to edit your java files
eclipse {
classpath {
plusConfigurations += configurations.embeddedJetty 
// for gradle 2.5 use: plusConfigurations += [configurations.embeddedJetty]
}
}


Then, you have to create the main class to start our embedded Jetty (in this example, I’ve called it ‘com.embedded.JettyStarter'):


public class JettyStarter {
public static void main(String[] args) throws Exception{
                       ProtectionDomain domain = JettyStarter.class.getProtectionDomain();
                       URL location = domain.getCodeSource().getLocation();
       
                       // create a web app and configure it to the root context of the server
                       WebAppContext webapp = new WebAppContext();
                       webapp.setDescriptor("WEB-INF/web.xml");
                       webapp.setConfigurations(new Configuration[]{ new AnnotationConfiguration()
        , new WebXmlConfiguration(), new WebInfConfiguration(), new MetaInfConfiguration()                });
                       webapp.setContextPath("/");
                       webapp.setWar(location.toExternalForm());
       
                       // starts the embedded server and bind it on 8081 port
     Server server = new Server(8081);
                       server.setHandler(webapp);        
                       server.start();
                       server.join();
}
}


Once both the build and the main file are implemented, you can forget it is an embedded war and add as many JSPs and Servlets as you want. In order to show an example of an servlet being executed, I’ve added the following TimeServlet (which simply prints the current date).


@SuppressWarnings("serial")
@WebServlet(urlPatterns = { "/time" })
public class TimeServlet extends HttpServlet {
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                 throws ServletException, IOException
        {
ServletOutputStream out = response.getOutputStream();
out.print(new Date().toString());
out.flush();
        }
}


and the following index.jsp, which prints a jsp expression in order to show a jsp being executed.


<html>
<head>
</head>
<body>
<h1>Hello: <%= "This is a jsp expression" %></h1>
<a href="time">Click here to execute TimeServlet</a>
</body>
</html>


Now, you can build your war and execute it using the following command line: ‘java -jar MyEmbeddedWar.war’. This will start the Jetty server in port 8081, so you can access the http://localhost:8081/ url in the browser.

The source code can be found here.

Good luck!