diff --git a/labs/vertx-akka/.gitignore b/labs/vertx-akka/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..2e78b79d531f0c06fd8b54a7a2d39b6511ce1e6d
--- /dev/null
+++ b/labs/vertx-akka/.gitignore
@@ -0,0 +1,51 @@
+.metadata
+.project
+.classpath
+.settings
+.history
+.worksheet
+gen
+**/*.swp
+**/*~.nib
+**/build/
+**/*.pbxuser
+**/*.perspective
+**/*.perspectivev3
+*.xcworkspace
+*.xcuserdatad
+*.iml
+project/*.ipr
+project/*.iml
+project/*.iws
+project/out
+project/*/target
+project/target
+project/*/bin
+project/*/build
+project/*.iml
+project/*/*.iml
+project/.idea
+project/.idea/*
+.idea/
+.DS_Store
+project/.DS_Store
+project/*/.DS_Store
+tm.out
+tmlog*.log
+*.tm*.epoch
+out/
+provisioning/.vagrant
+provisioning/*/.vagrant
+provisioning/*/*.known
+/sbt/akka-patterns-store/
+/daemon/src/build/
+*.lock
+logs/
+tmp/
+build/
+akka-patterns-store/
+lib_managed/
+.cache
+bin/
+.vertx/
+target/
diff --git a/labs/vertx-akka/README.md b/labs/vertx-akka/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/labs/vertx-akka/build.sbt b/labs/vertx-akka/build.sbt
new file mode 100755
index 0000000000000000000000000000000000000000..ae9f47785f15f61bc9e0e4502cc27e5fb2287d3c
--- /dev/null
+++ b/labs/vertx-akka/build.sbt
@@ -0,0 +1,99 @@
+enablePlugins(JavaServerAppPackaging)
+
+name := "vertx-akka"
+
+organization := "org.bigbluebutton"
+
+version := "0.0.2"
+
+scalaVersion  := "2.11.6"
+
+scalacOptions ++= Seq(
+  "-unchecked",
+  "-deprecation",
+  "-Xlint",
+  "-Ywarn-dead-code",
+  "-language:_",
+  "-target:jvm-1.8",
+  "-encoding", "UTF-8"
+)
+
+resolvers ++= Seq(
+  "spray repo" at "http://repo.spray.io/",
+  "rediscala" at "http://dl.bintray.com/etaty/maven",
+  "blindside-repos" at "http://blindside.googlecode.com/svn/repository/"
+)
+
+publishTo := Some(Resolver.file("file",  new File(Path.userHome.absolutePath+"/dev/repo/maven-repo/releases" )) )
+
+// We want to have our jar files in lib_managed dir.
+// This way we'll have the right path when we import
+// into eclipse.
+retrieveManaged := true
+
+unmanagedResourceDirectories in Compile += { baseDirectory.value / "src/main/webapp" }
+
+testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "html", "console", "junitxml")
+
+testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/scalatest-reports")
+
+libraryDependencies ++= {
+  val akkaVersion  = "2.4.0"
+  Seq(
+	  "com.typesafe.akka"        %%  "akka-actor"        % akkaVersion,
+	  "com.typesafe.akka"        %%  "akka-testkit"      % akkaVersion    % "test",
+	  "com.typesafe.akka" 	     %%  "akka-slf4j"        % akkaVersion,
+	  "ch.qos.logback"    	      %  "logback-classic"   % "1.0.13" % "runtime",
+	  "org.pegdown" 		      %  "pegdown"           % "1.4.0",
+	  "junit" 				      %  "junit"             % "4.11",
+	  "commons-codec"             %  "commons-codec"     % "1.8",
+	  "joda-time"                 %  "joda-time"         % "2.3",
+	  "com.google.code.gson"      %  "gson"              % "1.7.1",
+	  "io.vertx"                  %  "vertx-web"         % "3.1.0",
+	  "io.vertx"                  %  "vertx-auth-common" % "3.1.0",
+	  "io.vertx"                  %  "vertx-auth-shiro" % "3.1.0"
+	)}
+
+seq(Revolver.settings: _*)
+
+scalariformSettings
+
+
+//-----------
+// Packaging
+//
+// Reference:
+// https://github.com/muuki88/sbt-native-packager-examples/tree/master/akka-server-app
+// http://www.scala-sbt.org/sbt-native-packager/index.html
+//-----------
+mainClass := Some("org.bigbluebutton.Boot")
+
+maintainer in Linux := "Richard Alam <ritzalam@gmail.com>"
+
+packageSummary in Linux := "vertx akka example"
+
+packageDescription := """Vertx Akka Example."""
+
+val user = "bigbluebutton"
+
+val group = "bigbluebutton"
+
+// user which will execute the application
+daemonUser in Linux := user        
+
+// group which will execute the application
+daemonGroup in Linux := group 
+
+mappings in Universal <+= (packageBin in Compile, sourceDirectory ) map { (_, src) =>
+    // Move the application.conf so the user can override settings here
+    val appConf = src / "main" / "resources" / "application.conf"
+    appConf -> "conf/application.conf"
+}
+
+mappings in Universal <+= (packageBin in Compile, sourceDirectory ) map { (_, src) =>
+    // Move logback.xml so the user can override settings here    
+    val logConf = src / "main" / "resources" / "logback.xml"
+    logConf -> "conf/logback.xml"
+}
+
+debianPackageDependencies in Debian ++= Seq("java8-runtime-headless", "bash")
diff --git a/labs/vertx-akka/nginx/vertx-akka b/labs/vertx-akka/nginx/vertx-akka
new file mode 100644
index 0000000000000000000000000000000000000000..c9ba908c380aa68bca7957d7bac72831327aa98f
--- /dev/null
+++ b/labs/vertx-akka/nginx/vertx-akka
@@ -0,0 +1,23 @@
+server {
+     listen   80;
+     server_name  192.168.23.33;
+
+     access_log  /var/log/nginx/vertx-akka.access.log;
+
+        # Vertx-Akka landing page.
+        location / {
+          root   /var/www/vertx-akka;
+          index  index.html index.htm;
+          expires 1m;
+        }
+
+        #error_page  404  /404.html;
+
+        # Redirect server error pages to the static page /50x.html
+        #
+        error_page   500 502 503 504  /50x.html;
+        location = /50x.html {
+                root   /var/www/nginx-default;
+        }
+}
+
diff --git a/labs/vertx-akka/nginx/webroot/chat.html b/labs/vertx-akka/nginx/webroot/chat.html
new file mode 100644
index 0000000000000000000000000000000000000000..e393d2ef123c330bf3239f3c8271270c622184b1
--- /dev/null
+++ b/labs/vertx-akka/nginx/webroot/chat.html
@@ -0,0 +1,71 @@
+<!--
+  #%L
+  distributed-chat-service
+  %%
+  Copyright (C) 2015 Zanclus Consulting
+  %%
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+  
+       http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  #L%
+  -->
+<html>
+<head>
+  <title>Distributed Chat Service</title>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
+  <script src="//cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script>
+  <script src="vertxbus.js"></script>
+  <style>
+    .inset {
+      box-shadow: inset 0 0 4px #000000;
+      -moz-box-shadow: inset 0 0 4px #000000;
+      -webkit-box-shadow: inset 0 0 4px #000000;
+      width: 400px;
+      border-width: 4px;
+      padding: 5px;
+    }
+
+    input.inset {
+      height: 40px;
+    }
+
+    div.inset {
+      height: 500px;
+      white-space: pre-wrap
+    }
+  </style>
+</head>
+<body>
+<script>
+  var eb = new vertx.EventBus("http://192.168.23.33:3001/eventbus");
+  eb.onopen = function () {
+    eb.registerHandler("chat.to.client", function (msg) {
+      $('#chat').append(msg + "\n");
+    });
+  };
+
+  function send(event) {
+    if (event.keyCode == 13 || event.which == 13) {
+      var message = $('#input').val();
+      if (message.length > 0) {
+        console.log($('#input'));
+        eb.publish("chat.to.server", message);
+        $('#input').val("");
+      }
+    }
+  }
+</script>
+<div id="chat" class="inset"></div>
+<input id="input" type="text" onkeydown="send(event)" class="inset">
+</body>
+</html>
diff --git a/labs/vertx-akka/nginx/webroot/index.html b/labs/vertx-akka/nginx/webroot/index.html
new file mode 100755
index 0000000000000000000000000000000000000000..f13421c94c1dacc1972cb32ba6e07d142b80acd5
--- /dev/null
+++ b/labs/vertx-akka/nginx/webroot/index.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+  <title></title>
+  <script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
+  <script src="//cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script>
+  <script src="vertxbus.js"></script>
+</head>
+
+<style>
+  .news {
+    font-size: 20pt;
+  }
+</style>
+
+<body>
+
+<div class="news">Latest news: </div><br>
+<div id="status" class="news"></div>
+
+<script>
+ 
+  var eb = new vertx.EventBus("http://192.168.23.33:3000/eventbus");
+
+  eb.onopen = function() {
+    eb.registerHandler("news-feed", function(msg) {  
+      var str = "<code>" + msg + "</code><br>";
+      $('#status').prepend(str);
+    })    
+  }        
+
+
+</script>
+
+</body>
+</html>
diff --git a/labs/vertx-akka/nginx/webroot/vertxbus.js b/labs/vertx-akka/nginx/webroot/vertxbus.js
new file mode 100755
index 0000000000000000000000000000000000000000..59df3af1ad0d1abe10af5989d2f1179f0e7d25d8
--- /dev/null
+++ b/labs/vertx-akka/nginx/webroot/vertxbus.js
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2014 Red Hat, Inc.
+ *
+ *  All rights reserved. This program and the accompanying materials
+ *  are made available under the terms of the Eclipse Public License v1.0
+ *  and Apache License v2.0 which accompanies this distribution.
+ *
+ *  The Eclipse Public License is available at
+ *  http://www.eclipse.org/legal/epl-v10.html
+ *
+ *  The Apache License v2.0 is available at
+ *  http://www.opensource.org/licenses/apache2.0.php
+ *
+ *  You may elect to redistribute this code under either of these licenses.
+ */
+
+/*
+ *   Copyright (c) 2011-2013 The original author or authors
+ *   ------------------------------------------------------
+ *   All rights reserved. This program and the accompanying materials
+ *   are made available under the terms of the Eclipse Public License v1.0
+ *   and Apache License v2.0 which accompanies this distribution.
+ *
+ *       The Eclipse Public License is available at
+ *       http://www.eclipse.org/legal/epl-v10.html
+ *
+ *       The Apache License v2.0 is available at
+ *       http://www.opensource.org/licenses/apache2.0.php
+ *
+ *   You may elect to redistribute this code under either of these licenses.
+ */
+
+var vertx = vertx || {};
+
+!function(factory) {
+  if (typeof define === "function" && define.amd) {
+    // Expose as an AMD module with SockJS dependency.
+    // "vertxbus" and "sockjs" names are used because
+    // AMD module names are derived from file names.
+    define("vertxbus", ["sockjs"], factory);
+  } else {
+    // No AMD-compliant loader
+    factory(SockJS);
+  }
+}(function(SockJS) {
+
+  vertx.EventBus = function(url, options) {
+  
+    var that = this;
+    var sockJSConn = new SockJS(url, undefined, options);
+    var handlerMap = {};
+    var replyHandlers = {};
+    var state = vertx.EventBus.CONNECTING;
+    var pingTimerID = null;
+    var pingInterval = null;
+    if (options) {
+      pingInterval = options['vertxbus_ping_interval'];
+    }
+    if (!pingInterval) {
+      pingInterval = 5000;
+    }
+  
+    that.onopen = null;
+    that.onclose = null;
+
+    that.send = function(address, message, replyHandler) {
+      sendOrPub("send", address, message, replyHandler)
+    }
+  
+    that.publish = function(address, message) {
+      sendOrPub("publish", address, message, null)
+    }
+  
+    that.registerHandler = function(address, handler) {
+      checkSpecified("address", 'string', address);
+      checkSpecified("handler", 'function', handler);
+      checkOpen();
+      var handlers = handlerMap[address];
+      if (!handlers) {
+        handlers = [handler];
+        handlerMap[address] = handlers;
+        // First handler for this address so we should register the connection
+        var msg = { type : "register",
+                    address: address };
+        sockJSConn.send(JSON.stringify(msg));
+      } else {
+        handlers[handlers.length] = handler;
+      }
+    }
+  
+    that.unregisterHandler = function(address, handler) {
+      checkSpecified("address", 'string', address);
+      checkSpecified("handler", 'function', handler);
+      checkOpen();
+      var handlers = handlerMap[address];
+      if (handlers) {
+        var idx = handlers.indexOf(handler);
+        if (idx != -1) handlers.splice(idx, 1);
+        if (handlers.length == 0) {
+          // No more local handlers so we should unregister the connection
+  
+          var msg = { type : "unregister",
+                      address: address};
+          sockJSConn.send(JSON.stringify(msg));
+          delete handlerMap[address];
+        }
+      }
+    }
+  
+    that.close = function() {
+      checkOpen();
+      state = vertx.EventBus.CLOSING;
+      sockJSConn.close();
+    }
+  
+    that.readyState = function() {
+      return state;
+    }
+  
+    sockJSConn.onopen = function() {
+      // Send the first ping then send a ping every pingInterval milliseconds
+      sendPing();
+      pingTimerID = setInterval(sendPing, pingInterval);
+      state = vertx.EventBus.OPEN;
+      if (that.onopen) {
+        that.onopen();
+      }
+    };
+  
+    sockJSConn.onclose = function() {
+      state = vertx.EventBus.CLOSED;
+      if (pingTimerID) clearInterval(pingTimerID);
+      if (that.onclose) {
+        that.onclose();
+      }
+    };
+  
+    sockJSConn.onmessage = function(e) {
+      var msg = e.data;
+      var json = JSON.parse(msg);
+      var type = json.type;
+      if (type === 'err') {
+        console.error("Error received on connection: " + json.body);
+        return;
+      }
+      var body = json.body;
+      var replyAddress = json.replyAddress;
+      var address = json.address;
+      var replyHandler;
+      if (replyAddress) {
+        replyHandler = function(reply, replyHandler) {
+          // Send back reply
+          that.send(replyAddress, reply, replyHandler);
+        };
+      }
+      var handlers = handlerMap[address];
+      if (handlers) {
+        // We make a copy since the handler might get unregistered from within the
+        // handler itself, which would screw up our iteration
+        var copy = handlers.slice(0);
+        for (var i  = 0; i < copy.length; i++) {
+          copy[i](body, replyHandler);
+        }
+      } else {
+        // Might be a reply message
+        var handler = replyHandlers[address];
+        if (handler) {
+          delete replyHandlers[address];
+          handler(body, replyHandler);
+        }
+      }
+    }
+
+    function sendPing() {
+      var msg = {
+        type: "ping"
+      }
+      sockJSConn.send(JSON.stringify(msg));
+    }
+  
+    function sendOrPub(sendOrPub, address, message, replyHandler) {
+      checkSpecified("address", 'string', address);
+      checkSpecified("replyHandler", 'function', replyHandler, true);
+      checkOpen();
+      var envelope = { type : sendOrPub,
+                       address: address,
+                       body: message };
+      if (replyHandler) {
+        var replyAddress = makeUUID();
+        envelope.replyAddress = replyAddress;
+        replyHandlers[replyAddress] = replyHandler;
+      }
+      var str = JSON.stringify(envelope);
+      sockJSConn.send(str);
+    }
+  
+    function checkOpen() {
+      if (state != vertx.EventBus.OPEN) {
+        throw new Error('INVALID_STATE_ERR');
+      }
+    }
+  
+    function checkSpecified(paramName, paramType, param, optional) {
+      if (!optional && !param) {
+        throw new Error("Parameter " + paramName + " must be specified");
+      }
+      if (param && typeof param != paramType) {
+        throw new Error("Parameter " + paramName + " must be of type " + paramType);
+      }
+    }
+  
+    function isFunction(obj) {
+      return !!(obj && obj.constructor && obj.call && obj.apply);
+    }
+  
+    function makeUUID(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
+        .replace(/[xy]/g,function(a,b){return b=Math.random()*16,(a=="y"?b&3|8:b|0).toString(16)})}
+  
+  }
+  
+  vertx.EventBus.CONNECTING = 0;
+  vertx.EventBus.OPEN = 1;
+  vertx.EventBus.CLOSING = 2;
+  vertx.EventBus.CLOSED = 3;
+
+  return vertx.EventBus;
+
+});
diff --git a/labs/vertx-akka/project/Build.scala b/labs/vertx-akka/project/Build.scala
new file mode 100755
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/labs/vertx-akka/project/build.properties b/labs/vertx-akka/project/build.properties
new file mode 100755
index 0000000000000000000000000000000000000000..a6e117b61042ee81c62ba3a0fc5210d9502944df
--- /dev/null
+++ b/labs/vertx-akka/project/build.properties
@@ -0,0 +1 @@
+sbt.version=0.13.8
diff --git a/labs/vertx-akka/project/packager.sbt b/labs/vertx-akka/project/packager.sbt
new file mode 100755
index 0000000000000000000000000000000000000000..d3f5a12faa99758192ecc4ed3fc22c9249232e86
--- /dev/null
+++ b/labs/vertx-akka/project/packager.sbt
@@ -0,0 +1 @@
+
diff --git a/labs/vertx-akka/project/plugins.sbt b/labs/vertx-akka/project/plugins.sbt
new file mode 100755
index 0000000000000000000000000000000000000000..87ef6448644c23eed22abe7453eefd0054ca02a7
--- /dev/null
+++ b/labs/vertx-akka/project/plugins.sbt
@@ -0,0 +1,7 @@
+addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2")
+
+addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.3.0")
+
+addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.2.0")
+
+addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.0")
diff --git a/labs/vertx-akka/server-keystore.jks b/labs/vertx-akka/server-keystore.jks
new file mode 100755
index 0000000000000000000000000000000000000000..3322b4548c06c7e7890460d70ebb97fe9fd22799
Binary files /dev/null and b/labs/vertx-akka/server-keystore.jks differ
diff --git a/labs/vertx-akka/src/main/java/.gitkeep b/labs/vertx-akka/src/main/java/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/AkkaToVertxGateway.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/AkkaToVertxGateway.java
new file mode 100755
index 0000000000000000000000000000000000000000..d651e3a496a553f260cb2f6671b01359292f8fef
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/AkkaToVertxGateway.java
@@ -0,0 +1,19 @@
+package org.bigbluebutton.vertx;
+
+import io.vertx.core.Vertx;
+
+public class AkkaToVertxGateway implements IAkkaToVertxGateway{
+
+  private final Vertx vertx;
+
+  public AkkaToVertxGateway(Vertx vertx) {
+    this.vertx = vertx;
+  }
+  
+  @Override
+  public void send(String json) {
+    vertx.eventBus().publish("to-vertx", json);
+    
+  }
+
+}
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/AuthenticateVerticle.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/AuthenticateVerticle.java
new file mode 100755
index 0000000000000000000000000000000000000000..5c43a9275aed86badca804a01d67a44530ec05df
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/AuthenticateVerticle.java
@@ -0,0 +1,54 @@
+package org.bigbluebutton.vertx;
+
+import io.vertx.core.AbstractVerticle;
+import io.vertx.ext.auth.AuthProvider;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.handler.*;
+import io.vertx.ext.web.sstore.LocalSessionStore;
+import org.bigbluebutton.VertxToAkkaGateway;
+
+public class AuthenticateVerticle extends AbstractVerticle {
+	  private final VertxToAkkaGateway gw;
+	  
+	  public AuthenticateVerticle(VertxToAkkaGateway gw) {
+	    this.gw = gw;
+	  }
+	  
+  @Override
+  public void start() throws Exception {
+
+    Router router = Router.router(vertx);
+
+    // We need cookies, sessions and request bodies
+    router.route().handler(CookieHandler.create());
+    router.route().handler(BodyHandler.create());
+    router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));
+
+    // Simple auth service which uses a properties file for user/role info
+    AuthProvider authProvider = new MyAuthProvider(vertx);
+
+    // We need a user session handler too to make sure the user is stored in the session between requests
+    router.route().handler(UserSessionHandler.create(authProvider));
+
+    // Any requests to URI starting '/private/' require login
+    router.route("/private/*").handler(RedirectAuthHandler.create(authProvider, "/loginpage.html"));
+
+    // Serve the static private pages from directory 'private'
+    router.route("/private/*").handler(StaticHandler.create().setCachingEnabled(false).setWebRoot("private"));
+
+    // Handles the actual login
+    router.route("/loginhandler").handler(FormLoginHandler.create(authProvider));
+
+    // Implement logout
+    router.route("/logout").handler(context -> {
+      context.clearUser();
+      // Redirect back to the index page
+      context.response().putHeader("location", "/").setStatusCode(302).end();
+    });
+
+    // Serve the non private static pages
+    router.route().handler(StaticHandler.create());
+
+    vertx.createHttpServer().requestHandler(router::accept).listen(4000);
+  }
+}
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/BbbApi.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/BbbApi.java
new file mode 100755
index 0000000000000000000000000000000000000000..2b549fbc89e1da5cdf67164f2c169197c6b86802
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/BbbApi.java
@@ -0,0 +1,91 @@
+package org.bigbluebutton.vertx;
+
+import io.vertx.core.AbstractVerticle;
+import io.vertx.core.MultiMap;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.core.json.JsonArray;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.BodyHandler;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="http://tfox.org">Tim Fox</a>
+ */
+public class BbbApi extends AbstractVerticle {
+
+
+  private Map<String, JsonObject> products = new HashMap<>();
+
+  @Override
+  public void start() {
+
+    setUpInitialData();
+
+    Router router = Router.router(vertx);
+
+    router.route().handler(BodyHandler.create());
+    router.get("/products/:productID").handler(this::handleGetProduct);
+    router.put("/products/:productID").handler(this::handleAddProduct);
+    router.get("/products").handler(this::handleListProducts);
+    router.get("/bigbluebutton/api/create").handler(this::handleListProducts);
+    
+    vertx.createHttpServer().requestHandler(router::accept).listen(4000);
+  }
+
+  private void handleGetProduct(RoutingContext routingContext) {
+    String productID = routingContext.request().getParam("productID");
+    HttpServerResponse response = routingContext.response();
+    if (productID == null) {
+      sendError(400, response);
+    } else {
+      JsonObject product = products.get(productID);
+      if (product == null) {
+        sendError(404, response);
+      } else {
+        response.putHeader("content-type", "application/json").end(product.encodePrettily());
+      }
+    }
+  }
+
+  private void handleAddProduct(RoutingContext routingContext) {
+    String productID = routingContext.request().getParam("productID");
+    HttpServerResponse response = routingContext.response();
+    if (productID == null) {
+      sendError(400, response);
+    } else {
+      JsonObject product = routingContext.getBodyAsJson();
+      if (product == null) {
+        sendError(400, response);
+      } else {
+        products.put(productID, product);
+        response.end();
+      }
+    }
+  }
+
+  private void handleListProducts(RoutingContext routingContext) {
+	MultiMap params = routingContext.request().params();
+	System.out.println("Name: " + params.get("name"));
+    JsonArray arr = new JsonArray();
+    products.forEach((k, v) -> arr.add(v));
+    routingContext.response().putHeader("content-type", "application/json").end(arr.encodePrettily());
+  }
+
+  private void sendError(int statusCode, HttpServerResponse response) {
+    response.setStatusCode(statusCode).end();
+  }
+
+  private void setUpInitialData() {
+    addProduct(new JsonObject().put("id", "prod3568").put("name", "Egg Whisk").put("price", 3.99).put("weight", 150));
+    addProduct(new JsonObject().put("id", "prod7340").put("name", "Tea Cosy").put("price", 5.99).put("weight", 100));
+    addProduct(new JsonObject().put("id", "prod8643").put("name", "Spatula").put("price", 1.00).put("weight", 80));
+  }
+
+  private void addProduct(JsonObject product) {
+    products.put(product.getString("id"), product);
+  }
+}
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/BbbUser.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/BbbUser.java
new file mode 100755
index 0000000000000000000000000000000000000000..ef476db9730c0b9f8b817c6e642a8be85001f737
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/BbbUser.java
@@ -0,0 +1,50 @@
+package org.bigbluebutton.vertx;
+
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Handler;
+import io.vertx.core.json.JsonObject;
+import io.vertx.core.json.JsonArray;
+import io.vertx.ext.auth.AbstractUser;
+import io.vertx.ext.auth.AuthProvider;
+import io.vertx.core.logging.Logger;
+import io.vertx.core.logging.LoggerFactory;
+import io.vertx.core.Future;
+
+public class BbbUser extends AbstractUser {
+  private static final Logger log = LoggerFactory.getLogger(BbbUser.class);
+
+  private JsonObject jwtToken;
+  private JsonArray permissions;
+   
+  public BbbUser(JsonObject jwtToken, JsonArray permissions) {
+    this.jwtToken = jwtToken;
+    this.permissions = permissions;
+  }
+
+  
+  @Override
+  public JsonObject principal() {
+    return jwtToken;
+  }
+
+  @Override
+  public void setAuthProvider(AuthProvider arg0) {
+    // NOOP - JWT tokens are self contained :)  
+  }
+
+  @Override
+  protected void doIsPermitted(String permission, Handler<AsyncResult<Boolean>> handler) {
+    if (permissions != null) {
+      for (Object jwtPermission : permissions) {
+        if (permission.equals(jwtPermission)) {
+          handler.handle(Future.succeededFuture(true));
+          return;
+        }
+      }
+    }
+
+    log.debug("User has no permission [" + permission + "]");
+    handler.handle(Future.succeededFuture(false));
+  }
+
+}
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/ChatVerticle.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/ChatVerticle.java
new file mode 100755
index 0000000000000000000000000000000000000000..0cf12e631034fce5b40f08d33ee5fba9ea1e2a98
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/ChatVerticle.java
@@ -0,0 +1,75 @@
+package org.bigbluebutton.vertx;
+
+import io.vertx.core.AbstractVerticle;
+import io.vertx.core.eventbus.EventBus;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.handler.StaticHandler;
+import io.vertx.ext.web.handler.sockjs.BridgeEventType;
+import io.vertx.ext.web.handler.sockjs.BridgeOptions;
+import io.vertx.ext.web.handler.sockjs.PermittedOptions;
+import io.vertx.ext.web.handler.sockjs.SockJSHandler;
+import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions;
+
+import java.text.DateFormat;
+import java.time.Instant;
+import java.util.Date;
+
+import org.bigbluebutton.VertxToAkkaGateway;
+
+
+public class ChatVerticle extends AbstractVerticle {
+  
+  private final VertxToAkkaGateway gw;
+  
+  public ChatVerticle(VertxToAkkaGateway gw) {
+    this.gw = gw;
+  }
+  
+  @Override
+  public void start() throws Exception {
+
+    Router router = Router.router(vertx);
+
+    // Allow events for the designated addresses in/out of the event bus bridge
+    BridgeOptions opts = new BridgeOptions()
+      .addInboundPermitted(new PermittedOptions().setAddress("chat.to.server"))
+      .addOutboundPermitted(new PermittedOptions().setAddress("chat.to.client"));
+
+    SockJSHandlerOptions options = new SockJSHandlerOptions().setHeartbeatInterval(2000);
+    
+    // Create the event bus bridge and add it to the router.
+    SockJSHandler ebHandler = SockJSHandler.create(vertx, options); 
+    router.route("/eventbus/*").handler(ebHandler);
+
+    // Create a router endpoint for the static content.
+    router.route().handler(StaticHandler.create());
+
+    ebHandler.bridge(opts, be -> {
+      if (be.type() == BridgeEventType.PUBLISH || be.type() == BridgeEventType.RECEIVE) {
+        if (be.rawMessage().getString("body").equals("armadillos")) {
+          // Reject it
+          be.complete(false);
+          return;
+        }
+      }
+      be.complete(true);
+    });
+    
+    // Start the web server and tell it to use the router to handle requests.
+    vertx.createHttpServer().requestHandler(router::accept).listen(3001);
+
+    EventBus eb = vertx.eventBus();
+
+    // Register to listen for messages coming IN to the server
+    eb.consumer("chat.to.server").handler(message -> {
+      // Create a timestamp string
+      String timestamp = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM).format(Date.from(Instant.now()));
+      // Send the message back out to all clients with the timestamp prepended.
+      gw.send(timestamp + ": " + message.body());
+    });
+
+    eb.consumer("to-vertx").handler(message -> {
+      eb.publish("chat.to.client", message.body());
+    });    
+  }
+}
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/HelloWorld.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/HelloWorld.java
new file mode 100755
index 0000000000000000000000000000000000000000..560bf81a9fea68c7f7e548a7bfc190c6fad386e7
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/HelloWorld.java
@@ -0,0 +1,31 @@
+package org.bigbluebutton.vertx;
+
+import org.bigbluebutton.VertxToAkkaGateway;
+
+import io.vertx.core.Vertx;
+
+public class HelloWorld {
+  
+  private final Vertx vertx;
+  private final VertxToAkkaGateway gw;
+  
+  public HelloWorld(Vertx vertx, VertxToAkkaGateway gw)  {
+    this.vertx = vertx;
+    this.gw = gw;
+  }
+  
+  public void startup() {
+    // Create an HTTP server which simply returns "Hello World!" to each request.
+    //Vertx.vertx().createHttpServer().requestHandler(req -> req.response().end("Hello World! from gradle.")).listen(3000);
+  
+    //vertx.deployVerticle(new ChatVerticle(gw));
+    //vertx.deployVerticle(new RealtimeVerticle());
+    //vertx.deployVerticle(new AuthenticateVerticle());
+    
+    vertx.deployVerticle(new PrivateVerticle(gw));
+    //vertx.deployVerticle(new SimpleREST());
+    //vertx.deployVerticle(new BbbApi());
+  }
+
+  
+}
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/IAkkaToVertxGateway.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/IAkkaToVertxGateway.java
new file mode 100755
index 0000000000000000000000000000000000000000..f53438117f62402808eced3215a000365174d628
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/IAkkaToVertxGateway.java
@@ -0,0 +1,6 @@
+package org.bigbluebutton.vertx;
+
+public interface IAkkaToVertxGateway {
+
+  void send(String json);
+}
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/IVertxToAkkaGateway.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/IVertxToAkkaGateway.java
new file mode 100755
index 0000000000000000000000000000000000000000..3f89a1af9ce21c83864d82af80f968a8088e3b60
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/IVertxToAkkaGateway.java
@@ -0,0 +1,6 @@
+package org.bigbluebutton.vertx;
+
+public interface IVertxToAkkaGateway {
+
+  void send(String json);
+}
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/MyAuthProvider.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/MyAuthProvider.java
new file mode 100755
index 0000000000000000000000000000000000000000..5473a9cd7a5e1e9cd2761cd64aa9fbd54cb127e1
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/MyAuthProvider.java
@@ -0,0 +1,45 @@
+package org.bigbluebutton.vertx;
+
+import org.bigbluebutton.VertxToAkkaGateway;
+
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Handler;
+import io.vertx.core.Vertx;
+import io.vertx.core.eventbus.DeliveryOptions;
+import io.vertx.core.json.JsonArray;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.auth.AuthProvider;
+import io.vertx.ext.auth.User;
+import io.vertx.core.Future;
+
+public class MyAuthProvider implements AuthProvider {
+
+	private final Vertx vertx;
+	
+	public MyAuthProvider(Vertx vertx) {
+		this.vertx = vertx;
+	}
+	
+  @Override
+  public void authenticate(JsonObject user, Handler<AsyncResult<User>> resultHandler) {
+    JsonObject object = new JsonObject();
+    object.put("foo", "bar").put("num", 123).put("mybool", true);
+    
+    JsonArray array = new JsonArray();
+    array.add("foo").add(123).add(false);
+    
+    DeliveryOptions options = new DeliveryOptions();
+    options.setSendTimeout(5000);
+    
+    vertx.eventBus().send("to-akka-gw", 
+    		"Yay! Someone kicked a ball across a patch of grass", 
+    		options, ar -> {
+    	  if (ar.succeeded()) {
+    	    System.out.println("Received reply: " + ar.result().body());
+        	System.out.println("Got Authenticated");
+        	resultHandler.handle(Future.succeededFuture(new BbbUser(object, array)));
+    	  }
+    	});
+  }
+
+}
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/PrivateVerticle.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/PrivateVerticle.java
new file mode 100755
index 0000000000000000000000000000000000000000..0370bc4b993759efcb35c2daf953b487b2990ab6
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/PrivateVerticle.java
@@ -0,0 +1,152 @@
+package org.bigbluebutton.vertx;
+
+import java.text.DateFormat;
+import java.time.Instant;
+import java.util.Date;
+
+import org.bigbluebutton.VertxToAkkaGateway;
+
+import io.vertx.core.json.JsonObject;
+import io.vertx.core.AbstractVerticle;
+import io.vertx.core.eventbus.EventBus;
+import io.vertx.core.http.HttpServerOptions;
+import io.vertx.core.net.JksOptions;
+import io.vertx.ext.auth.AuthProvider;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.Session;
+import io.vertx.ext.web.handler.*;
+import io.vertx.ext.web.handler.sockjs.BridgeEventType;
+import io.vertx.ext.web.handler.sockjs.BridgeOptions;
+import io.vertx.ext.web.handler.sockjs.PermittedOptions;
+import io.vertx.ext.web.handler.sockjs.SockJSHandler;
+import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions;
+import io.vertx.ext.web.sstore.LocalSessionStore;
+
+public class PrivateVerticle extends AbstractVerticle {
+  private final VertxToAkkaGateway gw;
+  
+  public PrivateVerticle(VertxToAkkaGateway gw) {
+    this.gw = gw;
+  }
+  
+  @Override
+  public void start() throws Exception {
+
+    Router router = Router.router(vertx);
+
+    // We need cookies, sessions and request bodies
+    router.route().handler(CookieHandler.create());
+    router.route().handler(BodyHandler.create());
+    router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));
+
+    // Simple auth service which uses a properties file for user/role info
+    //AuthProvider authProvider = new MyAuthProvider(vertx);
+
+    // We need a user session handler too to make sure the user is stored in the session between requests
+    //router.route().handler(UserSessionHandler.create(authProvider));
+
+    // Handles the actual login
+    //router.route("/loginhandler").handler(FormLoginHandler.create(authProvider));
+
+    router.route("/private/*").handler(routingContext -> {
+
+        // This will require a login
+
+        // This will have the value true
+        boolean isAuthenticated = routingContext.user() != null;
+
+        if (isAuthenticated) {
+          System.out.println("**** User is authenticated.");
+        } else {
+          System.out.println("**** User is NOT authenticated.");
+        }
+        Session session = routingContext.session();
+
+        Integer cnt = session.get("hitcount");
+        cnt = (cnt == null ? 0 : cnt) + 1;
+
+        session.put("hitcount", cnt);
+
+//        routingContext.response().putHeader("content-type", "text/html")
+//                                 .end("<html><body><h1>Hitcount: " + cnt + "</h1></body></html>");  
+        routingContext.next();
+        
+      });
+    
+    // Any requests to URI starting '/private/' require login
+    //router.route("/private/*").handler(RedirectAuthHandler.create(authProvider, "/loginpage.html"));
+
+    // Serve the static private pages from directory 'private'
+    //router.route("/private/*").handler(StaticHandler.create().setCachingEnabled(false).setWebRoot("private"));
+
+    // Implement logout
+    router.route("/logout").handler(context -> {
+      context.clearUser();
+      // Redirect back to the index page
+      context.response().putHeader("location", "/").setStatusCode(302).end();
+    });
+
+    // Allow events for the designated addresses in/out of the event bus bridge
+    BridgeOptions opts = new BridgeOptions()
+      .addInboundPermitted(new PermittedOptions().setAddress("chat.to.server"))
+      .addOutboundPermitted(new PermittedOptions().setAddress("chat.to.client"));
+
+    SockJSHandlerOptions options = new SockJSHandlerOptions().setHeartbeatInterval(2000);
+    
+    // Create the event bus bridge and add it to the router.
+    SockJSHandler ebHandler = SockJSHandler.create(vertx, options); 
+    
+    router.route("/eventbus/*").handler(ebHandler);
+
+//    SockJSHandlerFactory sockJsMessageHandler = new SockJSHandlerFactory();
+//    sockJsMessageHandler.setupHandler(ebHandler, opts);
+    
+    EventBus eb = vertx.eventBus();
+    
+    ebHandler.bridge(opts, be -> {
+      if (be.type() == BridgeEventType.SOCKET_CREATED) {
+        System.out.println("Socket create for session: " + be.socket().webSession().id() + " socketWriteId:" + be.socket().writeHandlerID());
+      } else if (be.type() == BridgeEventType.SOCKET_CLOSED) { 
+        System.out.println("Socket closed for: " + be.socket().webSession().id());
+      } else if (be.type() == BridgeEventType.REGISTER) {
+        System.out.println("Register for: " + be.socket().webSession().id() + " \n   " + be.rawMessage());
+        eb.consumer("to-vertx").handler(message -> {
+          System.out.println("**** response to " + be.socket().webSession().id() + " msg = " +  message.body());
+        });
+        gw.send(be.rawMessage().toString());
+      } else {
+        System.out.println("Message from: " + be.socket().webSession().id() + " \n   " + be.rawMessage());      
+      }
+      
+     // System.out.println("USER=" + be.socket().webUser().principal());
+      
+      be.complete(true);
+    });
+    
+    // Create a router endpoint for the static content.
+    router.route().handler(StaticHandler.create());
+   
+    // Start the web server and tell it to use the router to handle requests.
+    //vertx.createHttpServer(new HttpServerOptions().setSsl(true).setKeyStoreOptions(
+    //    new JksOptions().setPath("server-keystore.jks").setPassword("wibble")
+    //  )).requestHandler(router::accept).listen(3001);
+    vertx.createHttpServer().requestHandler(router::accept).listen(3001);
+
+    // Register to listen for messages coming IN to the server
+    eb.consumer("chat.to.server").handler(message -> {
+      // Create a timestamp string
+      String timestamp = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM).format(Date.from(Instant.now()));
+      // Send the message back out to all clients with the timestamp prepended.
+      gw.send(timestamp + ": " + message.body());
+      eb.publish("foofoofoo", message.body());
+    });
+
+    eb.consumer("to-vertx").handler(message -> {
+      eb.publish("chat.to.client", message.body());
+    }); 
+
+    
+    // Serve the non private static pages
+    router.route().handler(StaticHandler.create());
+  }
+}
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/RealtimeVerticle.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/RealtimeVerticle.java
new file mode 100755
index 0000000000000000000000000000000000000000..55cb70152bd63d4aca9e1f1398d507b656df5cbc
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/RealtimeVerticle.java
@@ -0,0 +1,44 @@
+package org.bigbluebutton.vertx;
+
+import io.vertx.core.AbstractVerticle;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.handler.StaticHandler;
+import io.vertx.ext.web.handler.sockjs.BridgeEventType;
+import io.vertx.ext.web.handler.sockjs.BridgeOptions;
+import io.vertx.ext.web.handler.sockjs.PermittedOptions;
+import io.vertx.ext.web.handler.sockjs.SockJSHandler;
+
+public class RealtimeVerticle extends AbstractVerticle {
+  @Override
+  public void start() throws Exception {
+
+    Router router = Router.router(vertx);
+
+    // Allow outbound traffic to the news-feed address
+
+    BridgeOptions options = new BridgeOptions().addOutboundPermitted(new PermittedOptions().setAddress("news-feed"));
+
+    router.route("/eventbus/*").handler(SockJSHandler.create(vertx).bridge(options, event -> {
+
+      // You can also optionally provide a handler like this which will be passed any events that occur on the bridge
+      // You can use this for monitoring or logging, or to change the raw messages in-flight.
+      // It can also be used for fine grained access control.
+
+      if (event.type() == BridgeEventType.SOCKET_CREATED) {
+        System.out.println("A socket was created");
+      }
+
+      // This signals that it's ok to process the event
+      event.complete(true);
+
+    }));
+
+    // Serve the static resources
+    router.route().handler(StaticHandler.create());
+
+    vertx.createHttpServer().requestHandler(router::accept).listen(3000);
+
+    // Publish a message to the address "news-feed" every second
+    vertx.setPeriodic(1000, t -> vertx.eventBus().publish("news-feed", "news from the server!"));
+  }
+}
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/SimpleREST.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/SimpleREST.java
new file mode 100755
index 0000000000000000000000000000000000000000..e71dd5f448b393e097ff765be1559767a816f2ef
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/SimpleREST.java
@@ -0,0 +1,87 @@
+package org.bigbluebutton.vertx;
+
+import io.vertx.core.AbstractVerticle;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.core.json.JsonArray;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.BodyHandler;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="http://tfox.org">Tim Fox</a>
+ */
+public class SimpleREST extends AbstractVerticle {
+
+
+  private Map<String, JsonObject> products = new HashMap<>();
+
+  @Override
+  public void start() {
+
+    setUpInitialData();
+
+    Router router = Router.router(vertx);
+
+    router.route().handler(BodyHandler.create());
+    router.get("/products/:productID").handler(this::handleGetProduct);
+    router.put("/products/:productID").handler(this::handleAddProduct);
+    router.get("/products").handler(this::handleListProducts);
+
+    vertx.createHttpServer().requestHandler(router::accept).listen(4000);
+  }
+
+  private void handleGetProduct(RoutingContext routingContext) {
+    String productID = routingContext.request().getParam("productID");
+    HttpServerResponse response = routingContext.response();
+    if (productID == null) {
+      sendError(400, response);
+    } else {
+      JsonObject product = products.get(productID);
+      if (product == null) {
+        sendError(404, response);
+      } else {
+        response.putHeader("content-type", "application/json").end(product.encodePrettily());
+      }
+    }
+  }
+
+  private void handleAddProduct(RoutingContext routingContext) {
+    String productID = routingContext.request().getParam("productID");
+    HttpServerResponse response = routingContext.response();
+    if (productID == null) {
+      sendError(400, response);
+    } else {
+      JsonObject product = routingContext.getBodyAsJson();
+      if (product == null) {
+        sendError(400, response);
+      } else {
+        products.put(productID, product);
+        response.end();
+      }
+    }
+  }
+
+  private void handleListProducts(RoutingContext routingContext) {
+    JsonArray arr = new JsonArray();
+    products.forEach((k, v) -> arr.add(v));
+    routingContext.response().putHeader("content-type", "application/json").end(arr.encodePrettily());
+  }
+
+  private void sendError(int statusCode, HttpServerResponse response) {
+    response.setStatusCode(statusCode).end();
+  }
+
+  private void setUpInitialData() {
+    addProduct(new JsonObject().put("id", "prod3568").put("name", "Egg Whisk").put("price", 3.99).put("weight", 150));
+    addProduct(new JsonObject().put("id", "prod7340").put("name", "Tea Cosy").put("price", 5.99).put("weight", 100));
+    addProduct(new JsonObject().put("id", "prod8643").put("name", "Spatula").put("price", 1.00).put("weight", 80));
+  }
+
+  private void addProduct(JsonObject product) {
+    products.put(product.getString("id"), product);
+  }
+}
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/SockJSHandlerFactory.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/SockJSHandlerFactory.java
new file mode 100755
index 0000000000000000000000000000000000000000..560dad665f35fc12fa28c4d61cf9989f66786f74
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/SockJSHandlerFactory.java
@@ -0,0 +1,24 @@
+package org.bigbluebutton.vertx;
+
+import io.vertx.ext.web.handler.sockjs.BridgeEventType;
+import io.vertx.ext.web.handler.sockjs.BridgeOptions;
+import io.vertx.ext.web.handler.sockjs.SockJSHandler;
+
+public class SockJSHandlerFactory {
+
+  public  SockJSHandler setupHandler(SockJSHandler ebHandler, BridgeOptions opts) {
+
+    ebHandler.bridge(opts, be -> {
+      if (be.type() == BridgeEventType.SOCKET_CREATED) {
+        System.out.println("Socket create for: " + be.socket().webSession().id());
+      } else if (be.type() == BridgeEventType.SOCKET_CLOSED) {
+        System.out.println("Socket closed for: " + be.socket().webSession().id());
+      } else {
+        System.out.println("Message from: " + be.socket().webSession().id() + " \n   " + be.rawMessage());
+      }
+      be.complete(true);
+    });
+    
+    return ebHandler;
+  }
+}
diff --git a/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/VertxToAkkaBus.java b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/VertxToAkkaBus.java
new file mode 100755
index 0000000000000000000000000000000000000000..d64539f3814034a88321f072322465b2f143a70f
--- /dev/null
+++ b/labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/VertxToAkkaBus.java
@@ -0,0 +1,39 @@
+package org.bigbluebutton.vertx;
+
+
+import io.vertx.core.Vertx;
+import io.vertx.core.eventbus.Message;
+import io.vertx.core.eventbus.MessageConsumer;
+import io.vertx.core.json.JsonObject;
+
+import org.bigbluebutton.VertxToAkkaGateway;
+
+public class VertxToAkkaBus {
+
+	
+	public VertxToAkkaBus(Vertx vertx, VertxToAkkaGateway gw) {
+		
+		MessageConsumer<String> consumer = 
+				vertx.eventBus().consumer("to-akka-gw");
+		
+		consumer.handler(message -> {
+			System.out.println("I have received a message: " + message.body());
+			if (message.replyAddress() != null) {
+				String replyChannel = "reply-channel";
+				MessageConsumer<String> replyConsumer = 
+						vertx.eventBus().consumer(replyChannel);
+				replyConsumer.handler(replyMessage -> {
+				    	System.out.println("Got Authenticated");
+				    	
+				    	message.reply(replyMessage.body().toString());
+				    	replyConsumer.unregister();
+				    });
+				  gw.sendWithReply(message.body().toString(), replyChannel);   
+			  } else {
+				  gw.send(message.body().toString());
+			  }
+		  
+		});
+		
+	}
+}
diff --git a/labs/vertx-akka/src/main/resources/README b/labs/vertx-akka/src/main/resources/README
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/labs/vertx-akka/src/main/resources/application.conf b/labs/vertx-akka/src/main/resources/application.conf
new file mode 100755
index 0000000000000000000000000000000000000000..1d1430f5e1300f67217777ddab4fad2a76936b04
--- /dev/null
+++ b/labs/vertx-akka/src/main/resources/application.conf
@@ -0,0 +1,34 @@
+akka {
+  actor {
+    debug {
+      # enable DEBUG logging of all AutoReceiveMessages (Kill, PoisonPill et.c.)
+      autoreceive = on
+      # enable DEBUG logging of actor lifecycle changes
+      lifecycle = on
+    }
+  }
+  loggers = ["akka.event.slf4j.Slf4jLogger"]
+  loglevel = "INFO"
+  
+  rediscala-publish-worker-dispatcher {
+      mailbox-type = "akka.dispatch.SingleConsumerOnlyUnboundedMailbox"
+      # Throughput defines the maximum number of messages to be
+      # processed per actor before the thread jumps to the next actor.
+      # Set to 1 for as fair as possible.
+      throughput = 512
+    }
+    
+  rediscala-subscriber-worker-dispatcher {
+      mailbox-type = "akka.dispatch.SingleConsumerOnlyUnboundedMailbox"
+      # Throughput defines the maximum number of messages to be
+      # processed per actor before the thread jumps to the next actor.
+      # Set to 1 for as fair as possible.
+      throughput = 512
+    }
+}
+
+redis {
+    host="127.0.0.1"
+    port=6379
+    password=""
+}
diff --git a/labs/vertx-akka/src/main/resources/logback.xml b/labs/vertx-akka/src/main/resources/logback.xml
new file mode 100755
index 0000000000000000000000000000000000000000..267781bba2dd60859c0902fe61e8312243c0acbb
--- /dev/null
+++ b/labs/vertx-akka/src/main/resources/logback.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+  <encoder>
+    <pattern>%date{ISO8601} %-5level %logger{36} - %msg%n</pattern>
+  </encoder>
+</appender>
+
+  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+    <File>logs/bbb-apps-akka.log</File>
+    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+      <FileNamePattern>logs/bbb-apps-akka.%d{yyyy-MM-dd}.log</FileNamePattern>
+      <!-- keep 30 days worth of history -->
+      <MaxHistory>5</MaxHistory>
+    </rollingPolicy>
+    <layout class="ch.qos.logback.classic.PatternLayout">
+      <Pattern>%d{"yyyy-MM-dd HH:mm:ss,SSSXXX"} [%thread] %-5level %logger{35} - %msg%n</Pattern>
+    </layout>
+  </appender>
+      
+    <logger name="akka" level="INFO" />
+    <logger name="org.bigbluebutton" level="DEBUG" />
+
+    <root level="DEBUG">
+        <appender-ref ref="STDOUT"/>
+        <appender-ref ref="FILE" />
+    </root>
+</configuration>
\ No newline at end of file
diff --git a/labs/vertx-akka/src/main/scala/org/bigbluebutton/AuthService.scala b/labs/vertx-akka/src/main/scala/org/bigbluebutton/AuthService.scala
new file mode 100755
index 0000000000000000000000000000000000000000..aa7d7b11ca13ac83e8f38080bbe246acbefa9dc7
--- /dev/null
+++ b/labs/vertx-akka/src/main/scala/org/bigbluebutton/AuthService.scala
@@ -0,0 +1,24 @@
+package org.bigbluebutton
+
+import io.vertx.core.Vertx
+import akka.actor._
+import akka.actor.ActorLogging
+import org.bigbluebutton.vertx.IAkkaToVertxGateway
+import org.bigbluebutton.vertx.AkkaToVertxGateway
+
+object AuthService {
+  def props(gw: AkkaToVertxGateway): Props =
+    Props(classOf[AuthService], gw)
+}
+
+class AuthService(gw: AkkaToVertxGateway)
+    extends Actor with ActorLogging {
+
+  def receive = {
+    case msg: String => {
+      println("****** Authenticating " + msg)
+      sender ! "Let `em in!"
+    }
+    case _ => log.error("Cannot handle message ")
+  }
+}
\ No newline at end of file
diff --git a/labs/vertx-akka/src/main/scala/org/bigbluebutton/Boot.scala b/labs/vertx-akka/src/main/scala/org/bigbluebutton/Boot.scala
new file mode 100755
index 0000000000000000000000000000000000000000..b21cbaa918d1669c35e6d3c2ae3ee968d1413acf
--- /dev/null
+++ b/labs/vertx-akka/src/main/scala/org/bigbluebutton/Boot.scala
@@ -0,0 +1,30 @@
+package org.bigbluebutton
+
+import akka.actor.{ ActorSystem, Props }
+import scala.concurrent.duration._
+import scala.concurrent.{ Future, Await }
+import scala.concurrent.ExecutionContext.Implicits.global
+import org.bigbluebutton.vertx.HelloWorld
+import io.vertx.core.Vertx
+import org.bigbluebutton.vertx.AkkaToVertxGateway
+import org.bigbluebutton.vertx.IVertxToAkkaGateway
+import org.bigbluebutton.vertx.VertxToAkkaBus
+
+object Boot extends App with SystemConfiguration {
+
+  implicit val system = ActorSystem("vertx-akka-system")
+
+  val vertx = Vertx.vertx()
+
+  val vertxGW = new AkkaToVertxGateway(vertx)
+
+  val echoActor = system.actorOf(EchoService.props(vertxGW), "echo-actor")
+  val authActor = system.actorOf(AuthService.props(vertxGW), "auth-actor")
+
+  val akkaGW = new VertxToAkkaGateway(system, vertx, authActor, echoActor)
+  val vertxToAkkaBus = new VertxToAkkaBus(vertx, akkaGW)
+
+  val hello = new HelloWorld(vertx, akkaGW);
+  hello.startup()
+
+}
\ No newline at end of file
diff --git a/labs/vertx-akka/src/main/scala/org/bigbluebutton/EchoService.scala b/labs/vertx-akka/src/main/scala/org/bigbluebutton/EchoService.scala
new file mode 100755
index 0000000000000000000000000000000000000000..1df6f134c41ffb5d0bac076b18aa418ff7ed72a2
--- /dev/null
+++ b/labs/vertx-akka/src/main/scala/org/bigbluebutton/EchoService.scala
@@ -0,0 +1,23 @@
+package org.bigbluebutton
+
+import io.vertx.core.Vertx
+import akka.actor._
+import akka.actor.ActorLogging
+import org.bigbluebutton.vertx.IAkkaToVertxGateway
+import org.bigbluebutton.vertx.AkkaToVertxGateway
+
+object EchoService {
+  def props(gw: AkkaToVertxGateway): Props =
+    Props(classOf[EchoService], gw)
+}
+
+class EchoService(gw: AkkaToVertxGateway) extends Actor with ActorLogging {
+
+  def receive = {
+    case msg: String => {
+      println("****** Echoing " + msg)
+      gw.send(msg)
+    }
+    case _ => log.error("Cannot handle message ")
+  }
+}
\ No newline at end of file
diff --git a/labs/vertx-akka/src/main/scala/org/bigbluebutton/SystemConfiguration.scala b/labs/vertx-akka/src/main/scala/org/bigbluebutton/SystemConfiguration.scala
new file mode 100755
index 0000000000000000000000000000000000000000..663c885576f2a232677c7ae3aff22646eff5d84b
--- /dev/null
+++ b/labs/vertx-akka/src/main/scala/org/bigbluebutton/SystemConfiguration.scala
@@ -0,0 +1,13 @@
+package org.bigbluebutton
+
+import com.typesafe.config.ConfigFactory
+import scala.util.Try
+
+trait SystemConfiguration {
+
+  val config = ConfigFactory.load()
+
+  lazy val redisHost = Try(config.getString("redis.host")).getOrElse("127.0.0.1")
+  lazy val redisPort = Try(config.getInt("redis.port")).getOrElse(6379)
+  lazy val redisPassword = Try(config.getString("redis.password")).getOrElse("")
+}
\ No newline at end of file
diff --git a/labs/vertx-akka/src/main/scala/org/bigbluebutton/VertxToAkkaGateway.scala b/labs/vertx-akka/src/main/scala/org/bigbluebutton/VertxToAkkaGateway.scala
new file mode 100755
index 0000000000000000000000000000000000000000..01976ea0338bfe0218421996dc3826a36eb94318
--- /dev/null
+++ b/labs/vertx-akka/src/main/scala/org/bigbluebutton/VertxToAkkaGateway.scala
@@ -0,0 +1,49 @@
+package org.bigbluebutton
+
+import org.bigbluebutton.vertx.IAkkaToVertxGateway
+import akka.pattern.{ ask, pipe }
+import akka.util.Timeout
+import scala.concurrent.duration._
+import scala.util.Success
+import scala.util.Failure
+import akka.actor.ActorRef
+import io.vertx.core.Vertx
+import io.vertx.core.eventbus.MessageConsumer
+import io.vertx.core.Handler
+import io.vertx.core.eventbus.Message
+import akka.actor.ActorSystem
+
+class VertxToAkkaGateway(system: ActorSystem, vertx: Vertx,
+    authService: ActorRef,
+    echoService: ActorRef) extends IAkkaToVertxGateway {
+  implicit def executionContext = system.dispatcher
+
+  val consumer: MessageConsumer[String] = vertx.eventBus().consumer("foofoofoo")
+
+  def handle(m: Message[String]) = println(m.body())
+
+  consumer.handler(new MyHandler())
+
+  def sendWithReply(json: String, replyChannel: String) {
+    val future = authService.ask(json)(5 seconds)
+
+    future onComplete {
+      case Success(result) => {
+        vertx.eventBus().send("reply-channel", "You can come in.")
+      }
+      case Failure(failure) => {
+        vertx.eventBus().send("reply-channel", "You can NOT come in.")
+      }
+    }
+  }
+
+  def send(json: String) {
+    echoService ! json
+  }
+}
+
+class MyHandler extends Handler[Message[String]] {
+  def handle(message: Message[String]) = {
+    println("My Handler " + message.body())
+  }
+}
\ No newline at end of file
diff --git a/labs/vertx-akka/src/main/webapp/private/chat.html b/labs/vertx-akka/src/main/webapp/private/chat.html
new file mode 100755
index 0000000000000000000000000000000000000000..3caf52f2e56267a805e8d7e03d3571e14be62ff8
--- /dev/null
+++ b/labs/vertx-akka/src/main/webapp/private/chat.html
@@ -0,0 +1,76 @@
+<!--
+  #%L
+  distributed-chat-service
+  %%
+  Copyright (C) 2015 Zanclus Consulting
+  %%
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+  
+       http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  #L%
+  -->
+<html>
+<head>
+  <title>Distributed Chat Service</title>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
+  <script src="//cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script>
+  <script src="vertxbus.js"></script>
+  <style>
+    .inset {
+      box-shadow: inset 0 0 4px #000000;
+      -moz-box-shadow: inset 0 0 4px #000000;
+      -webkit-box-shadow: inset 0 0 4px #000000;
+      width: 400px;
+      border-width: 4px;
+      padding: 5px;
+    }
+
+    input.inset {
+      height: 40px;
+    }
+
+    div.inset {
+      height: 500px;
+      white-space: pre-wrap
+    }
+  </style>
+</head>
+<body>
+<script>
+  //var eb = new vertx.EventBus("https://192.168.23.33:3001/eventbus");
+  var eb = new vertx.EventBus("http://192.168.246.131:3001/eventbus");
+  eb.onopen = function () {
+    eb.registerHandler("chat.to.client", function (msg) {
+      $('#chat').append(msg + "\n");
+    });
+    
+    eb.send("foo-bar", "ValidateAuthToken", function(msg) {
+      $('#chat').append("reply: " + msg + "\n");
+    });
+  };
+
+  function send(event) {
+    if (event.keyCode == 13 || event.which == 13) {
+      var message = $('#input').val();
+      if (message.length > 0) {
+        console.log($('#input'));
+        eb.publish("chat.to.server", message);
+        $('#input').val("");
+      }
+    }
+  }
+</script>
+<div id="chat" class="inset"></div>
+<input id="input" type="text" onkeydown="send(event)" class="inset">
+</body>
+</html>
diff --git a/labs/vertx-akka/src/main/webapp/private/private_page.html b/labs/vertx-akka/src/main/webapp/private/private_page.html
new file mode 100755
index 0000000000000000000000000000000000000000..9f57655c64b97b75f278deb51cc737255eae39ff
--- /dev/null
+++ b/labs/vertx-akka/src/main/webapp/private/private_page.html
@@ -0,0 +1,19 @@
+<html>
+
+<head>
+    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/>
+    <meta http-equiv="Pragma" content="no-cache"/>
+    <meta http-equiv="Expires" content="0"/>
+</head>
+
+<body>
+
+<h2>You can only see this page if you are logged in!</h2>
+
+<br>
+<br>
+<a href="/logout">Logout</a>
+
+</body>
+
+</html>
\ No newline at end of file
diff --git a/labs/vertx-akka/src/main/webapp/private/vertxbus.js b/labs/vertx-akka/src/main/webapp/private/vertxbus.js
new file mode 100755
index 0000000000000000000000000000000000000000..59df3af1ad0d1abe10af5989d2f1179f0e7d25d8
--- /dev/null
+++ b/labs/vertx-akka/src/main/webapp/private/vertxbus.js
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2014 Red Hat, Inc.
+ *
+ *  All rights reserved. This program and the accompanying materials
+ *  are made available under the terms of the Eclipse Public License v1.0
+ *  and Apache License v2.0 which accompanies this distribution.
+ *
+ *  The Eclipse Public License is available at
+ *  http://www.eclipse.org/legal/epl-v10.html
+ *
+ *  The Apache License v2.0 is available at
+ *  http://www.opensource.org/licenses/apache2.0.php
+ *
+ *  You may elect to redistribute this code under either of these licenses.
+ */
+
+/*
+ *   Copyright (c) 2011-2013 The original author or authors
+ *   ------------------------------------------------------
+ *   All rights reserved. This program and the accompanying materials
+ *   are made available under the terms of the Eclipse Public License v1.0
+ *   and Apache License v2.0 which accompanies this distribution.
+ *
+ *       The Eclipse Public License is available at
+ *       http://www.eclipse.org/legal/epl-v10.html
+ *
+ *       The Apache License v2.0 is available at
+ *       http://www.opensource.org/licenses/apache2.0.php
+ *
+ *   You may elect to redistribute this code under either of these licenses.
+ */
+
+var vertx = vertx || {};
+
+!function(factory) {
+  if (typeof define === "function" && define.amd) {
+    // Expose as an AMD module with SockJS dependency.
+    // "vertxbus" and "sockjs" names are used because
+    // AMD module names are derived from file names.
+    define("vertxbus", ["sockjs"], factory);
+  } else {
+    // No AMD-compliant loader
+    factory(SockJS);
+  }
+}(function(SockJS) {
+
+  vertx.EventBus = function(url, options) {
+  
+    var that = this;
+    var sockJSConn = new SockJS(url, undefined, options);
+    var handlerMap = {};
+    var replyHandlers = {};
+    var state = vertx.EventBus.CONNECTING;
+    var pingTimerID = null;
+    var pingInterval = null;
+    if (options) {
+      pingInterval = options['vertxbus_ping_interval'];
+    }
+    if (!pingInterval) {
+      pingInterval = 5000;
+    }
+  
+    that.onopen = null;
+    that.onclose = null;
+
+    that.send = function(address, message, replyHandler) {
+      sendOrPub("send", address, message, replyHandler)
+    }
+  
+    that.publish = function(address, message) {
+      sendOrPub("publish", address, message, null)
+    }
+  
+    that.registerHandler = function(address, handler) {
+      checkSpecified("address", 'string', address);
+      checkSpecified("handler", 'function', handler);
+      checkOpen();
+      var handlers = handlerMap[address];
+      if (!handlers) {
+        handlers = [handler];
+        handlerMap[address] = handlers;
+        // First handler for this address so we should register the connection
+        var msg = { type : "register",
+                    address: address };
+        sockJSConn.send(JSON.stringify(msg));
+      } else {
+        handlers[handlers.length] = handler;
+      }
+    }
+  
+    that.unregisterHandler = function(address, handler) {
+      checkSpecified("address", 'string', address);
+      checkSpecified("handler", 'function', handler);
+      checkOpen();
+      var handlers = handlerMap[address];
+      if (handlers) {
+        var idx = handlers.indexOf(handler);
+        if (idx != -1) handlers.splice(idx, 1);
+        if (handlers.length == 0) {
+          // No more local handlers so we should unregister the connection
+  
+          var msg = { type : "unregister",
+                      address: address};
+          sockJSConn.send(JSON.stringify(msg));
+          delete handlerMap[address];
+        }
+      }
+    }
+  
+    that.close = function() {
+      checkOpen();
+      state = vertx.EventBus.CLOSING;
+      sockJSConn.close();
+    }
+  
+    that.readyState = function() {
+      return state;
+    }
+  
+    sockJSConn.onopen = function() {
+      // Send the first ping then send a ping every pingInterval milliseconds
+      sendPing();
+      pingTimerID = setInterval(sendPing, pingInterval);
+      state = vertx.EventBus.OPEN;
+      if (that.onopen) {
+        that.onopen();
+      }
+    };
+  
+    sockJSConn.onclose = function() {
+      state = vertx.EventBus.CLOSED;
+      if (pingTimerID) clearInterval(pingTimerID);
+      if (that.onclose) {
+        that.onclose();
+      }
+    };
+  
+    sockJSConn.onmessage = function(e) {
+      var msg = e.data;
+      var json = JSON.parse(msg);
+      var type = json.type;
+      if (type === 'err') {
+        console.error("Error received on connection: " + json.body);
+        return;
+      }
+      var body = json.body;
+      var replyAddress = json.replyAddress;
+      var address = json.address;
+      var replyHandler;
+      if (replyAddress) {
+        replyHandler = function(reply, replyHandler) {
+          // Send back reply
+          that.send(replyAddress, reply, replyHandler);
+        };
+      }
+      var handlers = handlerMap[address];
+      if (handlers) {
+        // We make a copy since the handler might get unregistered from within the
+        // handler itself, which would screw up our iteration
+        var copy = handlers.slice(0);
+        for (var i  = 0; i < copy.length; i++) {
+          copy[i](body, replyHandler);
+        }
+      } else {
+        // Might be a reply message
+        var handler = replyHandlers[address];
+        if (handler) {
+          delete replyHandlers[address];
+          handler(body, replyHandler);
+        }
+      }
+    }
+
+    function sendPing() {
+      var msg = {
+        type: "ping"
+      }
+      sockJSConn.send(JSON.stringify(msg));
+    }
+  
+    function sendOrPub(sendOrPub, address, message, replyHandler) {
+      checkSpecified("address", 'string', address);
+      checkSpecified("replyHandler", 'function', replyHandler, true);
+      checkOpen();
+      var envelope = { type : sendOrPub,
+                       address: address,
+                       body: message };
+      if (replyHandler) {
+        var replyAddress = makeUUID();
+        envelope.replyAddress = replyAddress;
+        replyHandlers[replyAddress] = replyHandler;
+      }
+      var str = JSON.stringify(envelope);
+      sockJSConn.send(str);
+    }
+  
+    function checkOpen() {
+      if (state != vertx.EventBus.OPEN) {
+        throw new Error('INVALID_STATE_ERR');
+      }
+    }
+  
+    function checkSpecified(paramName, paramType, param, optional) {
+      if (!optional && !param) {
+        throw new Error("Parameter " + paramName + " must be specified");
+      }
+      if (param && typeof param != paramType) {
+        throw new Error("Parameter " + paramName + " must be of type " + paramType);
+      }
+    }
+  
+    function isFunction(obj) {
+      return !!(obj && obj.constructor && obj.call && obj.apply);
+    }
+  
+    function makeUUID(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
+        .replace(/[xy]/g,function(a,b){return b=Math.random()*16,(a=="y"?b&3|8:b|0).toString(16)})}
+  
+  }
+  
+  vertx.EventBus.CONNECTING = 0;
+  vertx.EventBus.OPEN = 1;
+  vertx.EventBus.CLOSING = 2;
+  vertx.EventBus.CLOSED = 3;
+
+  return vertx.EventBus;
+
+});
diff --git a/labs/vertx-akka/src/main/webapp/webroot/index.html b/labs/vertx-akka/src/main/webapp/webroot/index.html
new file mode 100755
index 0000000000000000000000000000000000000000..f4106abb7605f76a432054f0d37f2f409ca43c88
--- /dev/null
+++ b/labs/vertx-akka/src/main/webapp/webroot/index.html
@@ -0,0 +1,15 @@
+<html>
+
+<body>
+
+<h1>Web site with public and private pages</h1>
+
+<br>
+<br>
+<a href="private/chat.html">Private page - login is required</a>
+
+<br><br>
+
+<a href="page1.html">Public page - no login required</a>
+</body>
+</html>
diff --git a/labs/vertx-akka/src/main/webapp/webroot/loginpage.html b/labs/vertx-akka/src/main/webapp/webroot/loginpage.html
new file mode 100755
index 0000000000000000000000000000000000000000..8549635298ea2d1381e9e8a03313b90619f97b52
--- /dev/null
+++ b/labs/vertx-akka/src/main/webapp/webroot/loginpage.html
@@ -0,0 +1,29 @@
+<html>
+<head>
+    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/>
+    <meta http-equiv="Pragma" content="no-cache"/>
+    <meta http-equiv="Expires" content="0"/>
+</head>
+<body>
+<h2>Please login</h2><br>
+
+<br>
+(Username is 'tim', password is 'sausages')
+<br>
+<br>
+
+<form action="/loginhandler" method="post">
+    <div>
+        <label>Username:</label>
+        <input type="text" name="username"/>
+    </div>
+    <div>
+        <label>Password:</label>
+        <input type="password" name="password"/>
+    </div>
+    <div>
+        <input type="submit" value="Log In"/>
+    </div>
+</form>
+</body>
+</html>
\ No newline at end of file
diff --git a/labs/vertx-akka/src/main/webapp/webroot/page1.html b/labs/vertx-akka/src/main/webapp/webroot/page1.html
new file mode 100755
index 0000000000000000000000000000000000000000..3ad26717225f5a41702e378b5e5aa7a3e4280a1d
--- /dev/null
+++ b/labs/vertx-akka/src/main/webapp/webroot/page1.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+    <title></title>
+</head>
+<body>
+<h1>Welcome to page1!</h1>
+
+<br>
+This page does not require login - anyone can see it
+</body>
+</html>
\ No newline at end of file
diff --git a/labs/vertx-akka/src/main/webapp/webroot/realtime.html b/labs/vertx-akka/src/main/webapp/webroot/realtime.html
new file mode 100755
index 0000000000000000000000000000000000000000..83bf5f519b8b9965aaa53fc0c543feab927fd52c
--- /dev/null
+++ b/labs/vertx-akka/src/main/webapp/webroot/realtime.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+  <title></title>
+  <script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
+  <script src="https://cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script>
+  <script src="vertxbus.js"></script>
+</head>
+
+<style>
+  .news {
+    font-size: 20pt;
+  }
+</style>
+
+<body>
+
+<div class="news">Latest news: </div><br>
+<div id="status" class="news"></div>
+
+<script>
+ 
+  var eb = new vertx.EventBus("http://192.168.23.33:3000/eventbus");
+
+  eb.onopen = function() {
+    eb.registerHandler("news-feed", function(msg) {  
+      var str = "<code>" + msg + "</code><br>";
+      $('#status').prepend(str);
+    })    
+  }        
+
+
+</script>
+
+</body>
+</html>
diff --git a/labs/vertx-akka/src/main/webapp/webroot/vertxbus.js b/labs/vertx-akka/src/main/webapp/webroot/vertxbus.js
new file mode 100755
index 0000000000000000000000000000000000000000..59df3af1ad0d1abe10af5989d2f1179f0e7d25d8
--- /dev/null
+++ b/labs/vertx-akka/src/main/webapp/webroot/vertxbus.js
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2014 Red Hat, Inc.
+ *
+ *  All rights reserved. This program and the accompanying materials
+ *  are made available under the terms of the Eclipse Public License v1.0
+ *  and Apache License v2.0 which accompanies this distribution.
+ *
+ *  The Eclipse Public License is available at
+ *  http://www.eclipse.org/legal/epl-v10.html
+ *
+ *  The Apache License v2.0 is available at
+ *  http://www.opensource.org/licenses/apache2.0.php
+ *
+ *  You may elect to redistribute this code under either of these licenses.
+ */
+
+/*
+ *   Copyright (c) 2011-2013 The original author or authors
+ *   ------------------------------------------------------
+ *   All rights reserved. This program and the accompanying materials
+ *   are made available under the terms of the Eclipse Public License v1.0
+ *   and Apache License v2.0 which accompanies this distribution.
+ *
+ *       The Eclipse Public License is available at
+ *       http://www.eclipse.org/legal/epl-v10.html
+ *
+ *       The Apache License v2.0 is available at
+ *       http://www.opensource.org/licenses/apache2.0.php
+ *
+ *   You may elect to redistribute this code under either of these licenses.
+ */
+
+var vertx = vertx || {};
+
+!function(factory) {
+  if (typeof define === "function" && define.amd) {
+    // Expose as an AMD module with SockJS dependency.
+    // "vertxbus" and "sockjs" names are used because
+    // AMD module names are derived from file names.
+    define("vertxbus", ["sockjs"], factory);
+  } else {
+    // No AMD-compliant loader
+    factory(SockJS);
+  }
+}(function(SockJS) {
+
+  vertx.EventBus = function(url, options) {
+  
+    var that = this;
+    var sockJSConn = new SockJS(url, undefined, options);
+    var handlerMap = {};
+    var replyHandlers = {};
+    var state = vertx.EventBus.CONNECTING;
+    var pingTimerID = null;
+    var pingInterval = null;
+    if (options) {
+      pingInterval = options['vertxbus_ping_interval'];
+    }
+    if (!pingInterval) {
+      pingInterval = 5000;
+    }
+  
+    that.onopen = null;
+    that.onclose = null;
+
+    that.send = function(address, message, replyHandler) {
+      sendOrPub("send", address, message, replyHandler)
+    }
+  
+    that.publish = function(address, message) {
+      sendOrPub("publish", address, message, null)
+    }
+  
+    that.registerHandler = function(address, handler) {
+      checkSpecified("address", 'string', address);
+      checkSpecified("handler", 'function', handler);
+      checkOpen();
+      var handlers = handlerMap[address];
+      if (!handlers) {
+        handlers = [handler];
+        handlerMap[address] = handlers;
+        // First handler for this address so we should register the connection
+        var msg = { type : "register",
+                    address: address };
+        sockJSConn.send(JSON.stringify(msg));
+      } else {
+        handlers[handlers.length] = handler;
+      }
+    }
+  
+    that.unregisterHandler = function(address, handler) {
+      checkSpecified("address", 'string', address);
+      checkSpecified("handler", 'function', handler);
+      checkOpen();
+      var handlers = handlerMap[address];
+      if (handlers) {
+        var idx = handlers.indexOf(handler);
+        if (idx != -1) handlers.splice(idx, 1);
+        if (handlers.length == 0) {
+          // No more local handlers so we should unregister the connection
+  
+          var msg = { type : "unregister",
+                      address: address};
+          sockJSConn.send(JSON.stringify(msg));
+          delete handlerMap[address];
+        }
+      }
+    }
+  
+    that.close = function() {
+      checkOpen();
+      state = vertx.EventBus.CLOSING;
+      sockJSConn.close();
+    }
+  
+    that.readyState = function() {
+      return state;
+    }
+  
+    sockJSConn.onopen = function() {
+      // Send the first ping then send a ping every pingInterval milliseconds
+      sendPing();
+      pingTimerID = setInterval(sendPing, pingInterval);
+      state = vertx.EventBus.OPEN;
+      if (that.onopen) {
+        that.onopen();
+      }
+    };
+  
+    sockJSConn.onclose = function() {
+      state = vertx.EventBus.CLOSED;
+      if (pingTimerID) clearInterval(pingTimerID);
+      if (that.onclose) {
+        that.onclose();
+      }
+    };
+  
+    sockJSConn.onmessage = function(e) {
+      var msg = e.data;
+      var json = JSON.parse(msg);
+      var type = json.type;
+      if (type === 'err') {
+        console.error("Error received on connection: " + json.body);
+        return;
+      }
+      var body = json.body;
+      var replyAddress = json.replyAddress;
+      var address = json.address;
+      var replyHandler;
+      if (replyAddress) {
+        replyHandler = function(reply, replyHandler) {
+          // Send back reply
+          that.send(replyAddress, reply, replyHandler);
+        };
+      }
+      var handlers = handlerMap[address];
+      if (handlers) {
+        // We make a copy since the handler might get unregistered from within the
+        // handler itself, which would screw up our iteration
+        var copy = handlers.slice(0);
+        for (var i  = 0; i < copy.length; i++) {
+          copy[i](body, replyHandler);
+        }
+      } else {
+        // Might be a reply message
+        var handler = replyHandlers[address];
+        if (handler) {
+          delete replyHandlers[address];
+          handler(body, replyHandler);
+        }
+      }
+    }
+
+    function sendPing() {
+      var msg = {
+        type: "ping"
+      }
+      sockJSConn.send(JSON.stringify(msg));
+    }
+  
+    function sendOrPub(sendOrPub, address, message, replyHandler) {
+      checkSpecified("address", 'string', address);
+      checkSpecified("replyHandler", 'function', replyHandler, true);
+      checkOpen();
+      var envelope = { type : sendOrPub,
+                       address: address,
+                       body: message };
+      if (replyHandler) {
+        var replyAddress = makeUUID();
+        envelope.replyAddress = replyAddress;
+        replyHandlers[replyAddress] = replyHandler;
+      }
+      var str = JSON.stringify(envelope);
+      sockJSConn.send(str);
+    }
+  
+    function checkOpen() {
+      if (state != vertx.EventBus.OPEN) {
+        throw new Error('INVALID_STATE_ERR');
+      }
+    }
+  
+    function checkSpecified(paramName, paramType, param, optional) {
+      if (!optional && !param) {
+        throw new Error("Parameter " + paramName + " must be specified");
+      }
+      if (param && typeof param != paramType) {
+        throw new Error("Parameter " + paramName + " must be of type " + paramType);
+      }
+    }
+  
+    function isFunction(obj) {
+      return !!(obj && obj.constructor && obj.call && obj.apply);
+    }
+  
+    function makeUUID(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
+        .replace(/[xy]/g,function(a,b){return b=Math.random()*16,(a=="y"?b&3|8:b|0).toString(16)})}
+  
+  }
+  
+  vertx.EventBus.CONNECTING = 0;
+  vertx.EventBus.OPEN = 1;
+  vertx.EventBus.CLOSING = 2;
+  vertx.EventBus.CLOSED = 3;
+
+  return vertx.EventBus;
+
+});
diff --git a/labs/vertx-akka/src/templates/etc-default b/labs/vertx-akka/src/templates/etc-default
new file mode 100755
index 0000000000000000000000000000000000000000..ce68a7ccfc47b3928f42050eae4639163e39286e
--- /dev/null
+++ b/labs/vertx-akka/src/templates/etc-default
@@ -0,0 +1 @@
+JAVA_OPTS="-Dconfig.file=${{chdir}}/conf/application.conf $JAVA_OPTS"
diff --git a/labs/vertx-akka/src/test/java/README b/labs/vertx-akka/src/test/java/README
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/labs/vertx-akka/src/test/resources/README b/labs/vertx-akka/src/test/resources/README
new file mode 100644
index 0000000000000000000000000000000000000000..2c6bd342ed02205663cd55e21bb3889809f2a23f
--- /dev/null
+++ b/labs/vertx-akka/src/test/resources/README
@@ -0,0 +1,8 @@
+
+1. Install httpie (https://github.com/jkbr/httpie)
+
+sudo apt-get install python-setuptools
+sudo easy_install httpie
+
+2. Send messages stored in messages/ directory
+  e.g. http POST 192.168.22.146:8989/meeting < create-meeting.json
diff --git a/labs/vertx-akka/src/test/resources/messages/create-meeting.json b/labs/vertx-akka/src/test/resources/messages/create-meeting.json
new file mode 100644
index 0000000000000000000000000000000000000000..cc7b3745f0fb7b58a7a21f84473a89b68c8a053d
--- /dev/null
+++ b/labs/vertx-akka/src/test/resources/messages/create-meeting.json
@@ -0,0 +1,49 @@
+	{
+	    "header": {
+	        "destination": {
+	            "to": "apps_channel"
+	        },
+	        "reply": {
+	            "to": "apps_channel",
+	            "correlation_id": "abc"
+	        },
+	        "name": "create_meeting_request",
+	        "timestamp": "2013-12-23T08:50Z",
+	        "source": "web-api"
+	    },
+	    "payload": {
+	        "meeting_descriptor": {
+	            "name": "English 101",
+	            "external_id": "english_101",
+	            "record": true,
+	            "welcome_message": "Welcome to English 101",
+	            "logout_url": "http://www.bigbluebutton.org",
+	            "avatar_url": "http://www.gravatar.com/bigbluebutton",
+	            "max_users": 20,
+	            "duration": {
+	                "length": 120,
+	                "allow_extend": false,
+	                "max": 240
+	            },
+	            "voice_conference": {
+	                "pin": 123456,
+	                "number": 85115
+	            },
+	            "phone_numbers": [
+	                {
+	                    "number": "613-520-7600",
+	                    "description": "Ottawa"
+	                },
+	                {
+	                    "number": "1-888-555-7890",
+	                    "description": "NA Toll-Free"
+	                }
+	            ],
+	            "metadata": {
+	                "customer_id": "acme-customer",
+	                "customer_name": "ACME"
+	            }
+	        }
+	    }
+	}   
+
diff --git a/labs/vertx-akka/src/test/resources/messages/register-user-request.json b/labs/vertx-akka/src/test/resources/messages/register-user-request.json
new file mode 100644
index 0000000000000000000000000000000000000000..e4c4d0bd6cc26c7f136ca4d275f7a5ea2e100e9c
--- /dev/null
+++ b/labs/vertx-akka/src/test/resources/messages/register-user-request.json
@@ -0,0 +1,30 @@
+{
+    "header": {
+        "destination": {
+            "to": "apps_channel"
+        },
+        "reply": {
+            "to": "apps_channel",
+            "correlation_id": "abc"
+        },
+        "name": "register_user_request",
+        "timestamp": "2013-12-23T08:50Z",
+        "source": "bbb-web"
+    },
+    "payload": {
+        "meeting": {
+            "name": "English 101",
+            "id": "english_101"
+        },
+        "session": "english_101-12345",
+        "user_descriptor": {
+            "external_id": "user1",
+            "name": "Guga",
+            "role": "MODERATOR",
+            "pin": 12345,
+            "welcome_message": "Welcome to English 101",
+            "logout_url": "http://www.example.com",
+            "avatar_url": "http://www.example.com/avatar.png"
+        }
+    }
+}
diff --git a/labs/vertx-akka/src/universal/conf/application.ini b/labs/vertx-akka/src/universal/conf/application.ini
new file mode 100644
index 0000000000000000000000000000000000000000..85777a6ed706bbae98b987fb9fc190b5eeef7e17
--- /dev/null
+++ b/labs/vertx-akka/src/universal/conf/application.ini
@@ -0,0 +1,42 @@
+# #################################
+# ##### Default configuration #####
+# #################################
+
+# Available replacements 
+# ------------------------------------------------
+# ${{author}}           debian author
+# ${{descr}}            debian package description
+# ${{exec}}             startup script name
+# ${{chdir}}            app directory
+# ${{retries}}          retries for startup
+# ${{retryTimeout}}     retry timeout
+# ${{app_name}}         normalized app name
+# ${{daemon_user}}      daemon user
+# -------------------------------------------------
+# DEPRECATED, use -J-Xmx1024m instead
+# -mem 1024
+
+# Setting -X directly (-J is stripped)
+# -J-X
+# -J-Xmx1024
+
+# Add additional jvm parameters
+# -Dkey=val
+
+# For play applications you may set
+# -Dpidfile.path=/var/run/${{app_name}}/play.pid
+
+# Turn on JVM debugging, open at the given port
+# -jvm-debug <port>  
+
+# Don't run the java version check
+# -no-version-check
+
+-J-Xms130m
+-J-Xmx256m
+
+# With universal:packageBin:
+#   - setup with a configuration tool after unzip
+#   - use the path to the application.ini file
+# -Dconfig.file=${{path_to}}/conf/application.conf
+