- add vertx-akka

This commit is contained in:
Richard Alam 2018-03-27 18:09:35 -07:00
parent 54fcaf70ef
commit 2315a1a353
49 changed files with 2211 additions and 0 deletions

51
labs/vertx-akka/.gitignore vendored Normal file
View File

@ -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/

View File

99
labs/vertx-akka/build.sbt Executable file
View File

@ -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")

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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;
});

View File

View File

@ -0,0 +1 @@
sbt.version=0.13.8

View File

@ -0,0 +1 @@

View File

@ -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")

Binary file not shown.

View File

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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());
});
}
}

View File

@ -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());
}
}

View File

@ -0,0 +1,6 @@
package org.bigbluebutton.vertx;
public interface IAkkaToVertxGateway {
void send(String json);
}

View File

@ -0,0 +1,6 @@
package org.bigbluebutton.vertx;
public interface IVertxToAkkaGateway {
void send(String json);
}

View File

@ -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)));
}
});
}
}

View File

@ -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());
}
}

View File

@ -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!"));
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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());
}
});
}
}

View File

@ -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=""
}

View File

@ -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>

View File

@ -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 ")
}
}

View File

@ -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()
}

View File

@ -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 ")
}
}

View File

@ -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("")
}

View File

@ -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())
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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;
});

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;
});

View File

@ -0,0 +1 @@
JAVA_OPTS="-Dconfig.file=${{chdir}}/conf/application.conf $JAVA_OPTS"

View File

View File

@ -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

View File

@ -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"
}
}
}
}

View File

@ -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"
}
}
}

View File

@ -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