- add vertx-akka
This commit is contained in:
parent
54fcaf70ef
commit
2315a1a353
51
labs/vertx-akka/.gitignore
vendored
Normal file
51
labs/vertx-akka/.gitignore
vendored
Normal 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/
|
0
labs/vertx-akka/README.md
Normal file
0
labs/vertx-akka/README.md
Normal file
99
labs/vertx-akka/build.sbt
Executable file
99
labs/vertx-akka/build.sbt
Executable 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")
|
23
labs/vertx-akka/nginx/vertx-akka
Normal file
23
labs/vertx-akka/nginx/vertx-akka
Normal 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;
|
||||
}
|
||||
}
|
||||
|
71
labs/vertx-akka/nginx/webroot/chat.html
Normal file
71
labs/vertx-akka/nginx/webroot/chat.html
Normal 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>
|
35
labs/vertx-akka/nginx/webroot/index.html
Executable file
35
labs/vertx-akka/nginx/webroot/index.html
Executable 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>
|
228
labs/vertx-akka/nginx/webroot/vertxbus.js
Executable file
228
labs/vertx-akka/nginx/webroot/vertxbus.js
Executable 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;
|
||||
|
||||
});
|
0
labs/vertx-akka/project/Build.scala
Executable file
0
labs/vertx-akka/project/Build.scala
Executable file
1
labs/vertx-akka/project/build.properties
Executable file
1
labs/vertx-akka/project/build.properties
Executable file
@ -0,0 +1 @@
|
||||
sbt.version=0.13.8
|
1
labs/vertx-akka/project/packager.sbt
Executable file
1
labs/vertx-akka/project/packager.sbt
Executable file
@ -0,0 +1 @@
|
||||
|
7
labs/vertx-akka/project/plugins.sbt
Executable file
7
labs/vertx-akka/project/plugins.sbt
Executable 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")
|
BIN
labs/vertx-akka/server-keystore.jks
Executable file
BIN
labs/vertx-akka/server-keystore.jks
Executable file
Binary file not shown.
0
labs/vertx-akka/src/main/java/.gitkeep
Normal file
0
labs/vertx-akka/src/main/java/.gitkeep
Normal 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
91
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/BbbApi.java
Executable file
91
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/BbbApi.java
Executable 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);
|
||||
}
|
||||
}
|
50
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/BbbUser.java
Executable file
50
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/BbbUser.java
Executable 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));
|
||||
}
|
||||
|
||||
}
|
75
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/ChatVerticle.java
Executable file
75
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/ChatVerticle.java
Executable 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());
|
||||
});
|
||||
}
|
||||
}
|
31
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/HelloWorld.java
Executable file
31
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/HelloWorld.java
Executable 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());
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
public interface IAkkaToVertxGateway {
|
||||
|
||||
void send(String json);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
public interface IVertxToAkkaGateway {
|
||||
|
||||
void send(String json);
|
||||
}
|
45
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/MyAuthProvider.java
Executable file
45
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/MyAuthProvider.java
Executable 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)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
152
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/PrivateVerticle.java
Executable file
152
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/PrivateVerticle.java
Executable 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());
|
||||
}
|
||||
}
|
44
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/RealtimeVerticle.java
Executable file
44
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/RealtimeVerticle.java
Executable 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!"));
|
||||
}
|
||||
}
|
87
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/SimpleREST.java
Executable file
87
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/SimpleREST.java
Executable 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
39
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/VertxToAkkaBus.java
Executable file
39
labs/vertx-akka/src/main/java/org/bigbluebutton/vertx/VertxToAkkaBus.java
Executable 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());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
0
labs/vertx-akka/src/main/resources/README
Normal file
0
labs/vertx-akka/src/main/resources/README
Normal file
34
labs/vertx-akka/src/main/resources/application.conf
Executable file
34
labs/vertx-akka/src/main/resources/application.conf
Executable 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=""
|
||||
}
|
28
labs/vertx-akka/src/main/resources/logback.xml
Executable file
28
labs/vertx-akka/src/main/resources/logback.xml
Executable 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>
|
24
labs/vertx-akka/src/main/scala/org/bigbluebutton/AuthService.scala
Executable file
24
labs/vertx-akka/src/main/scala/org/bigbluebutton/AuthService.scala
Executable 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 ")
|
||||
}
|
||||
}
|
30
labs/vertx-akka/src/main/scala/org/bigbluebutton/Boot.scala
Executable file
30
labs/vertx-akka/src/main/scala/org/bigbluebutton/Boot.scala
Executable 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()
|
||||
|
||||
}
|
23
labs/vertx-akka/src/main/scala/org/bigbluebutton/EchoService.scala
Executable file
23
labs/vertx-akka/src/main/scala/org/bigbluebutton/EchoService.scala
Executable 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 ")
|
||||
}
|
||||
}
|
13
labs/vertx-akka/src/main/scala/org/bigbluebutton/SystemConfiguration.scala
Executable file
13
labs/vertx-akka/src/main/scala/org/bigbluebutton/SystemConfiguration.scala
Executable 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("")
|
||||
}
|
49
labs/vertx-akka/src/main/scala/org/bigbluebutton/VertxToAkkaGateway.scala
Executable file
49
labs/vertx-akka/src/main/scala/org/bigbluebutton/VertxToAkkaGateway.scala
Executable 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())
|
||||
}
|
||||
}
|
76
labs/vertx-akka/src/main/webapp/private/chat.html
Executable file
76
labs/vertx-akka/src/main/webapp/private/chat.html
Executable 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>
|
19
labs/vertx-akka/src/main/webapp/private/private_page.html
Executable file
19
labs/vertx-akka/src/main/webapp/private/private_page.html
Executable 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>
|
228
labs/vertx-akka/src/main/webapp/private/vertxbus.js
Executable file
228
labs/vertx-akka/src/main/webapp/private/vertxbus.js
Executable 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;
|
||||
|
||||
});
|
15
labs/vertx-akka/src/main/webapp/webroot/index.html
Executable file
15
labs/vertx-akka/src/main/webapp/webroot/index.html
Executable 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>
|
29
labs/vertx-akka/src/main/webapp/webroot/loginpage.html
Executable file
29
labs/vertx-akka/src/main/webapp/webroot/loginpage.html
Executable 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>
|
11
labs/vertx-akka/src/main/webapp/webroot/page1.html
Executable file
11
labs/vertx-akka/src/main/webapp/webroot/page1.html
Executable 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>
|
35
labs/vertx-akka/src/main/webapp/webroot/realtime.html
Executable file
35
labs/vertx-akka/src/main/webapp/webroot/realtime.html
Executable 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>
|
228
labs/vertx-akka/src/main/webapp/webroot/vertxbus.js
Executable file
228
labs/vertx-akka/src/main/webapp/webroot/vertxbus.js
Executable 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;
|
||||
|
||||
});
|
1
labs/vertx-akka/src/templates/etc-default
Executable file
1
labs/vertx-akka/src/templates/etc-default
Executable file
@ -0,0 +1 @@
|
||||
JAVA_OPTS="-Dconfig.file=${{chdir}}/conf/application.conf $JAVA_OPTS"
|
0
labs/vertx-akka/src/test/java/README
Normal file
0
labs/vertx-akka/src/test/java/README
Normal file
8
labs/vertx-akka/src/test/resources/README
Normal file
8
labs/vertx-akka/src/test/resources/README
Normal 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
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
42
labs/vertx-akka/src/universal/conf/application.ini
Normal file
42
labs/vertx-akka/src/universal/conf/application.ini
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user