Merge remote-tracking branch 'upstream/master' into join-components-with-skeleton

Conflicts:
	bigbluebutton-html5/imports/api/verto/index.js
	bigbluebutton-html5/imports/ui/components/deskshare/service.js
This commit is contained in:
perroned 2016-06-15 20:07:42 +00:00
commit a95ed82293
1676 changed files with 109725 additions and 55532 deletions

2
.gitignore vendored
View File

@ -6,6 +6,8 @@ record-and-playback/playback-web/playback-web-0.1.war
record-and-playback/.project
push_to_git.py
**/target/*
**/.cache-main
**/.cache-tests
.vagrant/*
**/.settings/*
*/.gradle

579
DEVELOPMENT.md Normal file
View File

@ -0,0 +1,579 @@
This document provides instructions for developers to setup their
environment and work on the upcoming BBB 1.1 (tentative release version).
## Install BBB 1.0
Make sure you have a working BBB 1.0 before you proceed with the instructions below.
## Install OpenJDK 8
```
sudo add-apt-repository ppa:openjdk-r/ppa
sudo apt-get update
sudo apt-get install openjdk-8-jdk
```
Change the default jre. Choose Java 8.
```
sudo update-alternatives --config java
```
Change the default jdk. Choose Jdk8
```
sudo update-alternatives --config javac
```
## Environment Variables
Edit `~/.profile` and change `JAVA_HOME`
```
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
```
Save the file and refresh environment vars.
```
source ~/.profile
```
## Update Development Tools
### Install The Core Development Tools
```
sudo apt-get install git-core ant
```
### Install Gradle
```
cd ~/dev/tools
wget http://services.gradle.org/distributions/gradle-2.12-bin.zip
unzip gradle-2.12-bin.zip
ln -s gradle-2.12 gradle
```
### Install Grails
```
cd ~/dev/tools
wget https://github.com/grails/grails-core/releases/download/v2.5.2/grails-2.5.2.zip
unzip grails-2.5.2.zip
ln -s grails-2.5.2 grails
```
### Install Maven
```
cd ~/dev/tools
wget apache.parentingamerica.com//maven/maven-3/3.3.3/binaries/apache-maven-3.3.3-bin.zip
unzip apache-maven-3.3.3-bin.zip
ln -s apache-maven-3.3.3 maven
```
### Install sbt
```
cd ~/dev/tools
wget https://dl.bintray.com/sbt/native-packages/sbt/0.13.9/sbt-0.13.9.tgz
tar zxvf sbt-0.13.9.tgz
```
In the next step, you need to get the Apache Flex 4.13.0 SDK package.
**Note:** Even though we're downloading the Apache Flex 4.13.0 SDK, BigBlueButton is developed and built with Flex 3 compatibility mode enabled.
First, you need to download the SDK tarball from an Apache mirror site and then unpack it.
```
wget https://archive.apache.org/dist/flex/4.13.0/binaries/apache-flex-sdk-4.13.0-bin.tar.gz
tar xvfz apache-flex-sdk-4.13.0-bin.tar.gz
```
Once Flex SDK is unpacked, you need to download the Adobe Flex SDK. We'll do this step manually in case the download fails (if it does, remove the incomplete file and issue the `wget` command again).
```
cd apache-flex-sdk-4.13.0-bin/
mkdir -p in/
wget http://download.macromedia.com/pub/flex/sdk/builds/flex4.6/flex_sdk_4.6.0.23201B.zip -P in/
```
Once the SDK has downloaded, we can use its `build.xml` script to automatically download the remaining third-party tools.
```
ant -f frameworks/build.xml thirdparty-downloads
```
After Flex downloads the remaining third-party tools, you need to modify their permissions.
```
sudo find ~/dev/tools/apache-flex-sdk-4.13.0-bin -type d -exec chmod o+rx '{}' \;
chmod 755 ~/dev/tools/apache-flex-sdk-4.13.0-bin/bin/*
sudo chmod -R +r ~/dev/tools/apache-flex-sdk-4.13.0-bin
```
Next, create a linked directory with a shortened name for easier referencing.
```
ln -s ~/dev/tools/apache-flex-sdk-4.13.0-bin ~/dev/tools/flex
```
The next step in setting up the Flex SDK environment is to download a Flex library for video.
```
cd ~/dev/tools/
mkdir -p apache-flex-sdk-4.13.0-bin/frameworks/libs/player/11.2
cd apache-flex-sdk-4.13.0-bin/frameworks/libs/player/11.2
wget http://fpdownload.macromedia.com/get/flashplayer/installers/archive/playerglobal/playerglobal11_2.swc
mv -f playerglobal11_2.swc playerglobal.swc
```
The last step to have a working Flex SDK is to configure it to work with playerglobal 11.2
```
cd ~/dev/tools/apache-flex-sdk-4.13.0-bin
sudo sed -i "s/11.1/11.2/g" frameworks/flex-config.xml
sudo sed -i "s/<swf-version>14<\/swf-version>/<swf-version>15<\/swf-version>/g" frameworks/flex-config.xml
sudo sed -i "s/{playerglobalHome}\/{targetPlayerMajorVersion}.{targetPlayerMinorVersion}/libs\/player\/11.2/g" frameworks/flex-config.xml
```
With the tools installed, you need to add a set of environment variables to your `.profile` to access these tools.
```
vi ~/.profile
```
Copy-and-paste the following text at bottom of `.profile`.
```
export GRAILS_HOME=$HOME/dev/tools/grails
export PATH=$PATH:$GRAILS_HOME/bin
export FLEX_HOME=$HOME/dev/tools/flex
export PATH=$PATH:$FLEX_HOME/bin
export GRADLE_HOME=$HOME/dev/tools/gradle
export PATH=$PATH:$GRADLE_HOME/bin
export SBT_HOME=$HOME/dev/tools/sbt
export PATH=$PATH:$SBT_HOME/bin
export MAVEN_HOME=$HOME/dev/tools/maven
export PATH=$PATH:$MAVEN_HOME/bin
```
Reload your profile to use these tools (this will happen automatically when you next login).
```
source ~/.profile
```
Check that the tools are now in your path by running the following command.
```
$ mxmlc -version
Version 4.13.0 build 20140701
```
## Setup Red5
```
cd /usr/share
# Make a backup of the deployed red5
sudo mv red5 red5-orig
# Symlink red5 to old red5
sudo ln -s red5-orig red5
```
## Build Red5
Build red5-parent
```
cd ~/dev
git clone https://github.com/bigbluebutton/red5-parent.git
cd red5-parent/
git checkout snapshot-mar-30-2016
mvn install
```
Build red5-io
```
cd ~/dev
git clone https://github.com/bigbluebutton/red5-io.git
cd red5-io
git checkout snapshot-mar-30-2016
./bbb-build.sh
```
Build red5-server-common
```
cd ~/dev
git clone https://github.com/bigbluebutton/red5-server-common.git
cd red5-server-common
git checkout snapshot-mar-30-2016
./bbb-build.sh
```
Build red5-server
```
cd ~/dev
git clone https://github.com/bigbluebutton/red5-server.git
cd red5-server
git checkout snapshot-mar-30-2016
./build-red5.sh
# Deploy red5, this will copy the new red5 to /usr/share
# and modify the symlink you created above.
./deploy-red5.sh
```
# Developing the client
# Client Development
With the development environment checked out and the code cloned, we are ready to start developing!
This section will walk you through making a simple change to the BigBlueButton client.
## Setting up the environment
The first thing you need to do is to copy the template `config.xml` file to the build directory for the client.
```
cd ~/dev/bigbluebutton/
cp bigbluebutton-client/resources/config.xml.template bigbluebutton-client/src/conf/config.xml
```
The `config.xml` file is one of the first files loaded by the BigBlueButton client when it connects to the server. The `config.xml` file tells BigBlueButton client how to load the remaining components (such as chat module, deskshare module, video conf module, etc.) and sets a number of configuration parameters for each component. The `config.xml` specifies the hostname (or IP address) for loading each component.
Let's look at the first ten lines of the `config.xml` file you just copied.
```
$ head -n 10 bigbluebutton-client/src/conf/config.xml
<?xml version="1.0" ?>
<config>
<localeversion suppressWarning="false">0.9.0</localeversion>
<version>VERSION</version>
<help url="http://HOST/help.html"/>
<javaTest url="http://HOST/testjava.html"/>
<porttest host="HOST" application="video/portTest" timeout="10000"/>
<bwMon server="HOST" application="video/bwTest"/>
<application uri="rtmp://HOST/bigbluebutton" host="http://HOST/bigbluebutton/api/enter"/>
<language userSelectionEnabled="true" />
```
You will see the word `HOST` where there would be configured hostname/IP address. You need to change the text `HOST` to the IP address (or hostname) of your BigBlueButton server. For example, if the IP address of your BigBlueButton server is `192.168.1.145`, then using the following command you can easily substitute all occurrences of `HOST` with `192.168.1.145`.
Note: Don't copy-and-paste the following command as-is: the address `192.168.1.145` is likely not the correct IP address (or hostname) for your BigBlueButton server. Substitute the IP address (or hostname) for your BigBlueButton server.
```
sed -i s/HOST/192.168.1.145/g bigbluebutton-client/src/conf/config.xml
```
After you've done the above command, take a quick look at the file and ensure all instances of `HOST` are properly replaced with the IP address (or hostname) of your BigBlueButton server.
The `config.xml` is ultimately loaded by the BigBlueButton client when a user joins a session on the server.
Later on, when you deploy your modified client to the BigBlueButton server, there will be two BigBlueButton clients on your server: your modified BigBlueButton client and the default BigBlueButton packaged client (again, this is good as you can switch back and forth). However, the BigBlueButton configuration command `sudo bbb-conf ` only modifies the packaged BigBlueButton client and you will need to mirror any changes to the packaged config.xml to the secondary client's config.xml.
Next, you need to setup nginx to redirect calls to the client towards your development version. If you don't already have nginx client development file at `/etc/bigbluebutton/nginx/client_dev`, create one with the following command.
**NOTE:** Make sure to replace "firstuser" with your own username if it's different.
```
echo "
location /client/BigBlueButton.html {
root /home/firstuser/dev/bigbluebutton/bigbluebutton-client;
index index.html index.htm;
expires 1m;
}
# BigBlueButton Flash client.
location /client {
root /home/firstuser/dev/bigbluebutton/bigbluebutton-client;
index index.html index.htm;
}
" | sudo tee /etc/bigbluebutton/nginx/client_dev
```
Check the contents to ensure it matches below.
Again, make sure you change `/home/firstuser` to match your home directory.
```
$ cat /etc/bigbluebutton/nginx/client_dev
location /client/BigBlueButton.html {
root /home/firstuser/dev/bigbluebutton/bigbluebutton-client;
index index.html index.htm;
expires 1m;
}
# BigBlueButton Flash client.
location /client {
root /home/firstuser/dev/bigbluebutton/bigbluebutton-client;
index index.html index.htm;
}
```
These rules tell nginx where to find the BigBlueButton client. Currently, nginx is using the rules with the default BigBlueButton client through a symbolic link.
```
$ ls -al /etc/bigbluebutton/nginx/client.nginx
lrwxrwxrwx 1 root root 31 2013-05-05 15:44 /etc/bigbluebutton/nginx/client.nginx -> /etc/bigbluebutton/nginx/client
```
Modify this symbolic link so it points to the development directory for your BigBlueButton client.
```
sudo ln -f -s /etc/bigbluebutton/nginx/client_dev /etc/bigbluebutton/nginx/client.nginx
```
Check that the modifications are in place.
```
$ ls -al /etc/bigbluebutton/nginx/client.nginx
lrwxrwxrwx 1 root root 35 2013-05-05 21:07 /etc/bigbluebutton/nginx/client.nginx -> /etc/bigbluebutton/nginx/client_dev
```
Now we need to restart nginx so our changes take effect.
```
$ sudo service nginx restart
Restarting nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
configuration file /etc/nginx/nginx.conf test is successful nginx.
```
Now, when you launch the BigBlueButton client, nginx will serve the client from your development directory. Next, we need to rebuild the client.
## Building the client
Let's now build the client. Note we're going to build and run the client to make sure it works before making any changes to the source.
First, we'll build the locales (language translation files). If you are not modifying the locales, you only need to do this once.
```
cd ~/dev/bigbluebutton/bigbluebutton-client
```
Build build a specific locale (en_US default)
```
ant locale -DLOCALE=en_US
```
To build all locales
```
ant locales
```
This will take about 10 minutes (depending on the speed of your computer). Next, let's build the client
```
ant
```
This will create a build of the BigBlueButton client in the `/home/firstuser/dev/bigbluebutton/bigbluebutton-client/client` directory.
## Setup nginx
Create file `/etc/bigbluebutton/nginx/screenshare.nginx` and add the following:
```
# Handle desktop sharing. Forwards
# requests to Red5 on port 5080.
location /screenshare {
proxy_pass http://127.0.0.1:5080;
proxy_redirect default;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
include fastcgi_params;
}
```
Restart nginx
```
sudo service nginx restart
```
## Build BBB Red5 Applications
Turn off red5 service
```
sudo service bbb-red5 stop
```
You need to make `red5/webapps` writeable. Otherwise, you will get a permission error when you try to deploy into Red5.
```
sudo chmod -R 777 /usr/share/red5/webapps
```
### Build common-message
```
cd ~/dev/bigbluebutton/bbb-common-message/
sbt publish
sbt publishLocal
```
### Build bbb-apps
```
cd ~/dev/bigbluebutton/bigbluebutton-apps/
gradle resolveDeps
gradle clean war deploy
```
### Build bbb-voice
```
cd ~/dev/bigbluebutton/bbb-voice
```
Edit `src/main/webapp/WEB-INF/bigbluebutton-sip.properties`
Make sure the IP addresses point to yout BBB server.
```
bbb.sip.app.ip=192.168.74.128
# The ip and port of the FreeSWITCH server
freeswitch.ip=192.168.74.128
```
```
gradle resolveDeps
gradle clean war deploy
```
### Build bbb-video
```
cd ~/dev/bigbluebutton/bbb-video/
gradle resolveDeps
gradle clean war deploy
```
### Build bbb-screenshare
```
cd ~/dev/bigbluebutton/bbb-screenshare/app
```
Edit `src/main/webapp/WEB-INF/screenshare.properties`
Make sure the following points to your BBB server.
```
streamBaseUrl=rtmp://192.168.74.128/screenshare
jnlpUrl=http://192.168.74.128/screenshare
jnlpFile=http://192.168.74.128/screenshare/screenshare.jnlp
```
Build and deploy
```
./deploy.sh
```
## Stop services
```
sudo /etc/init.d/bbb-red5 stop
sudo service bbb-apps-akka stop
sudo service bbb-fsesl-akka stop
```
Remove old `bbb-web` app from tomcat
```
sudo rm /var/lib/tomcat7/webapps/bigbluebutton.war
```
## Manually start services
### Run Red5
Open up a terminal.
```
cd /usr/share/red5
sudo -u red5 ./red5.sh
```
### Run Akka Apps
Open up another terminal.
```
cd ~/dev/bigbluebutton/akka-bbb-apps
sbt run
```
### Run Akka FSESL App
Open another terminal
```
cd ~/dev/bigbluebutton/akka-bbb-fsesl
sbt run
```
### Run bbb-web
```
cd ~/dev/bigbluebutton/bigbluebutton-web
```
Get the salt and BBB URL from `/var/lib/tomcat7/webapps/demo/bbb_api_conf.jsp`
Edit `grails-app/conf/bigbluebutton.properties` and change the following with
the salt and IP you got from above.
```
bigbluebutton.web.serverURL=http://192.168.74.128
securitySalt=856d5e0197b1aa0cf79897841142a5f6
```
Start bbb-web
```
gradle resolveDeps
grails clean
grails -Dserver.port=8888 run-war
```
If things started without errors, congrats!

View File

@ -6,7 +6,7 @@ organization := "org.bigbluebutton"
version := "0.0.2"
scalaVersion := "2.11.6"
scalaVersion := "2.11.7"
scalacOptions ++= Seq(
"-unchecked",
@ -38,22 +38,32 @@ testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "html", "console",
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/scalatest-reports")
libraryDependencies ++= {
val akkaVersion = "2.3.11"
val akkaVersion = "2.3.14"
val akkaStreamV = "1.0"
val scalaTestV = "2.2.4"
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",
"com.etaty.rediscala" %% "rediscala" % "1.4.0",
"commons-codec" % "commons-codec" % "1.8",
"joda-time" % "joda-time" % "2.3",
"com.google.code.gson" % "gson" % "1.7.1",
"redis.clients" % "jedis" % "2.7.2",
"org.apache.commons" % "commons-lang3" % "3.2",
"org.bigbluebutton" % "bbb-common-message" % "0.0.17"
)}
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
"com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
"com.typesafe.akka" %% "akka-stream-experimental" % akkaStreamV,
"com.typesafe.akka" %% "akka-http-core-experimental" % akkaStreamV,
"com.typesafe.akka" %% "akka-http-experimental" % akkaStreamV,
"com.typesafe.akka" %% "akka-http-spray-json-experimental" % akkaStreamV,
"com.typesafe.akka" %% "akka-http-testkit-experimental" % akkaStreamV,
"org.scalatest" % "scalatest_2.11" % scalaTestV % "test",
"ch.qos.logback" % "logback-classic" % "1.0.13" % "runtime",
"org.pegdown" % "pegdown" % "1.4.0",
"junit" % "junit" % "4.11",
"com.etaty.rediscala" %% "rediscala" % "1.4.0",
"commons-codec" % "commons-codec" % "1.8",
"joda-time" % "joda-time" % "2.3",
"com.google.code.gson" % "gson" % "2.5",
"redis.clients" % "jedis" % "2.7.2",
"org.apache.commons" % "commons-lang3" % "3.2",
"org.bigbluebutton" % "bbb-common-message" % "0.0.18-SNAPSHOT",
"io.spray" %% "spray-json" % "1.3.2"
)
}
seq(Revolver.settings: _*)

View File

@ -0,0 +1,32 @@
import org.bigbluebutton.core.apps.BreakoutRoomModel
import org.bigbluebutton.core.apps.BreakoutRoomApp
object BreakoutRoom {
val breakoutModel = new BreakoutRoomModel //> breakoutModel : org.bigbluebutton.core.apps.BreakoutRoomModel = org.bigblue
//| button.core.apps.BreakoutRoomModel@721d4bd9
breakoutModel.createBreakoutRoom("1", "Room 1", "voice-1", Vector("user-1"), "default.pdf")
//> res0: org.bigbluebutton.core.apps.BreakoutRoom = BreakoutRoom(1,Room 1,voice
//| -1,Vector(user-1),Vector(),default.pdf)
breakoutModel.createBreakoutRoom("2", "Room 2", "voice-2", Vector("user-2"), "default.pdf")
//> res1: org.bigbluebutton.core.apps.BreakoutRoom = BreakoutRoom(2,Room 2,voice
//| -2,Vector(user-2),Vector(),default.pdf)
breakoutModel.getAssignedUsers("1") //> res2: Option[Vector[String]] = Some(Vector(user-1))
breakoutModel.getAssignedUsers("2") //> res3: Option[Vector[String]] = Some(Vector(user-2))
var breakoutRoomId = "1" //> breakoutRoomId : String = 1
breakoutModel.getAssignedUsers(breakoutRoomId) foreach { users =>
users.foreach { u =>
println(Vector(u, breakoutRoomId))
} //> Vector(user-1, 1)
}
breakoutRoomId = "2"
breakoutModel.getAssignedUsers(breakoutRoomId) foreach { users =>
users.foreach { u =>
println(Vector(u, breakoutRoomId))
} //> Vector(user-2, 2)
}
}

124
akka-bbb-apps/scala/JsonTest.sc Executable file
View File

@ -0,0 +1,124 @@
import org.bigbluebutton.core.api._
import scala.util.{ Try, Success, Failure }
import org.bigbluebutton.core.JsonMessageDecoder
import org.bigbluebutton.messages.BreakoutRoomsList
import org.bigbluebutton.messages.payload.BreakoutRoomsListPayload
import java.util.ArrayList
import org.bigbluebutton.core.messaging.Util
import org.bigbluebutton.messages.payload.BreakoutRoomPayload
import org.bigbluebutton.core.pubsub.senders.MeetingMessageToJsonConverter
import spray.json._
import DefaultJsonProtocol._
import com.google.gson.JsonArray
import scala.collection.mutable.ListBuffer
object JsonTest {
import org.bigbluebutton.core.UserMessagesProtocol._
import spray.json._
println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet
val xroom1 = new BreakoutRoomInPayload("foo", Vector("a", "b", "c"))
//> xroom1 : org.bigbluebutton.core.api.BreakoutRoomInPayload = BreakoutRoomInP
//| ayload(foo,Vector(a, b, c))
val xroom2 = new BreakoutRoomInPayload("bar", Vector("x", "y", "z"))
//> xroom2 : org.bigbluebutton.core.api.BreakoutRoomInPayload = BreakoutRoomInP
//| ayload(bar,Vector(x, y, z))
val xroom3 = new BreakoutRoomInPayload("baz", Vector("q", "r", "s"))
//> xroom3 : org.bigbluebutton.core.api.BreakoutRoomInPayload = BreakoutRoomInP
//| ayload(baz,Vector(q, r, s))
val xmsg = new CreateBreakoutRooms("test-meeting", 10, Vector(xroom1, xroom2, xroom3))
//> xmsg : org.bigbluebutton.core.api.CreateBreakoutRooms = CreateBreakoutRoom
//| s(test-meeting,10,Vector(BreakoutRoomInPayload(foo,Vector(a, b, c)), Breako
//| utRoomInPayload(bar,Vector(x, y, z)), BreakoutRoomInPayload(baz,Vector(q, r
//| , s))))
val xjsonAst = xmsg.toJson //> xjsonAst : spray.json.JsValue = {"meetingId":"test-meeting","durationInMin
//| utes":10,"rooms":[{"name":"foo","users":["a","b","c"]},{"name":"bar","users
//| ":["x","y","z"]},{"name":"baz","users":["q","r","s"]}]}
val xjson = xjsonAst.asJsObject //> xjson : spray.json.JsObject = {"meetingId":"test-meeting","durationInMinut
//| es":10,"rooms":[{"name":"foo","users":["a","b","c"]},{"name":"bar","users":
//| ["x","y","z"]},{"name":"baz","users":["q","r","s"]}]}
val meetingId = for {
meetingId <- xjson.fields.get("meetingId")
} yield meetingId //> meetingId : Option[spray.json.JsValue] = Some("test-meeting")
val cbrm = """
{"header":{"name":"CreateBreakoutRoomsRequest"},"payload":{"meetingId":"abc123","rooms":[{"name":"room1","users":["Tidora","Nidora","Tinidora"]},{"name":"room2","users":["Jose","Wally","Paolo"]},{"name":"room3","users":["Alden","Yaya Dub"]}],"durationInMinutes":20}}
""" //> cbrm : String = "
//| {"header":{"name":"CreateBreakoutRoomsRequest"},"payload":{"meetingId":"a
//| bc123","rooms":[{"name":"room1","users":["Tidora","Nidora","Tinidora"]},{"n
//| ame":"room2","users":["Jose","Wally","Paolo"]},{"name":"room3","users":["Al
//| den","Yaya Dub"]}],"durationInMinutes":20}}
//| "
JsonMessageDecoder.decode(cbrm) //> res0: Option[org.bigbluebutton.core.api.InMessage] = Some(CreateBreakoutRoo
//| ms(abc123,20,Vector(BreakoutRoomInPayload(room1,Vector(Tidora, Nidora, Tini
//| dora)), BreakoutRoomInPayload(room2,Vector(Jose, Wally, Paolo)), BreakoutRo
//| omInPayload(room3,Vector(Alden, Yaya Dub)))))
val rbju = """
{"header":{"name":"RequestBreakoutJoinURL"},"payload":{"userId":"id6pa5t8m1c9_1","meetingId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357","breakoutId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357-2"}}
""" //> rbju : String = "
//| {"header":{"name":"RequestBreakoutJoinURL"},"payload":{"userId":"id6pa5t8
//| m1c9_1","meetingId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357
//| ","breakoutId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357-2"}}
//|
//| "
JsonMessageDecoder.decode(rbju) //> res1: Option[org.bigbluebutton.core.api.InMessage] = Some(RequestBreakoutJo
//| inURLInMessage(183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357,183f0
//| bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357-2,id6pa5t8m1c9_1))
val brl = """
{"payload":{"meetingId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452849036428","rooms":{"startIndex":0,"endIndex":0,"focus":0,"dirty":false,"depth":0}},"header":{"timestamp":33619724,"name":"BreakoutRoomsList","current_time":1452849043547,"version":"0.0.1"}}
""" //> brl : String = "
//| {"payload":{"meetingId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452849
//| 036428","rooms":{"startIndex":0,"endIndex":0,"focus":0,"dirty":false,"depth
//| ":0}},"header":{"timestamp":33619724,"name":"BreakoutRoomsList","current_ti
//| me":1452849043547,"version":"0.0.1"}}
//| "
val brb = new BreakoutRoomBody("Breakout Room", "br-id-1");
//> brb : org.bigbluebutton.core.api.BreakoutRoomBody = BreakoutRoomBody(Break
//| out Room,br-id-1)
val jsObj = JsObject(
"name" -> JsString(brb.name),
"breakoutId" -> JsString(brb.breakoutId)) //> jsObj : spray.json.JsObject = {"name":"Breakout Room","breakoutId":"br-id-
//| 1"}
val vector = Vector(jsObj) //> vector : scala.collection.immutable.Vector[spray.json.JsObject] = Vector({
//| "name":"Breakout Room","breakoutId":"br-id-1"})
val brlum = new BreakoutRoomsListOutMessage("main-meeting-1", Vector(brb))
//> brlum : org.bigbluebutton.core.api.BreakoutRoomsListOutMessage = BreakoutR
//| oomsListOutMessage(main-meeting-1,Vector(BreakoutRoomBody(Breakout Room,br-
//| id-1)))
var roomsJsVector: ListBuffer[JsObject] = new ListBuffer[JsObject]()
//> roomsJsVector : scala.collection.mutable.ListBuffer[spray.json.JsObject] =
//| ListBuffer()
brlum.rooms.foreach { r =>
roomsJsVector.append(JsObject("name" -> JsString(r.name), "breakoutId" -> JsString(r.breakoutId)))
}
roomsJsVector.length //> res2: Int = 1
val jsonAst = List(1, 2, 3).toJson //> jsonAst : spray.json.JsValue = [1,2,3]
roomsJsVector.toList.toJson //> res3: spray.json.JsValue = [{"name":"Breakout Room","breakoutId":"br-id-1"}
//| ]
JsArray(roomsJsVector.toVector) //> res4: spray.json.JsArray = [{"name":"Breakout Room","breakoutId":"br-id-1"}
//| ]
MeetingMessageToJsonConverter.breakoutRoomsListOutMessageToJson(brlum);
//> res5: String = {"payload":{"meetingId":"main-meeting-1","rooms":[{"name":"B
//| reakout Room","breakoutId":"br-id-1"}]},"header":{"timestamp":47333494,"nam
//| e":"BreakoutRoomsList","current_time":1453399680291,"version":"0.0.1"}}
// JsonMessageDecoder.unmarshall(cbrm) match {
// case Success(validMsg) => println(validMsg)
// case Failure(ex) => println("Unhandled message: [{}]")
// }
}

67
akka-bbb-apps/scala/Test2.sc Executable file
View File

@ -0,0 +1,67 @@
import scala.collection.immutable.StringOps
import java.net.URLEncoder
import scala.collection._
object Test2 {
println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet
val userId = new StringOps("abc_12") //> userId : scala.collection.immutable.StringOps = abc_12
val s2 = userId.split('_') //> s2 : Array[String] = Array(abc, 12)
val s1 = if (s2.length == 2) s2(0) else userId //> s1 : Comparable[String] = abc
def sortParam(params: mutable.Map[String, String]):SortedSet[String] = {
collection.immutable.SortedSet[String]() ++ params.keySet
} //> sortParam: (params: scala.collection.mutable.Map[String,String])scala.collec
//| tion.SortedSet[String]
def createBaseString(params: mutable.Map[String, String]): String = {
val csbuf = new StringBuffer()
var keys = sortParam(params)
var first = true;
for (key <- keys) {
for (value <- params.get(key)) {
if (first) {
first = false;
} else {
csbuf.append("&");
}
csbuf.append(key);
csbuf.append("=");
csbuf.append(value);
}
}
return csbuf.toString();
} //> createBaseString: (params: scala.collection.mutable.Map[String,String])Strin
//| g
def urlEncode(s: String): String = {
URLEncoder.encode(s, "UTF-8");
} //> urlEncode: (s: String)String
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&password=mp&redirect=true"
//> baseString : String = fullName=User+4621018&isBreakout=true&meetingID=rand
//| om-1853792&password=mp&redirect=true
val params = new collection.mutable.HashMap[String, String]
//> params : scala.collection.mutable.HashMap[String,String] = Map()
params += "fullName" -> urlEncode("User 4621018")
//> res0: Test2.params.type = Map(fullName -> User+4621018)
params += "isBreakout" -> urlEncode("true") //> res1: Test2.params.type = Map(fullName -> User+4621018, isBreakout -> true)
//|
params += "meetingID" -> urlEncode("random-1853792")
//> res2: Test2.params.type = Map(fullName -> User+4621018, isBreakout -> true,
//| meetingID -> random-1853792)
params += "password" -> urlEncode("mp") //> res3: Test2.params.type = Map(fullName -> User+4621018, isBreakout -> true,
//| meetingID -> random-1853792, password -> mp)
params += "redirect" -> urlEncode("true") //> res4: Test2.params.type = Map(fullName -> User+4621018, isBreakout -> true,
//| meetingID -> random-1853792, redirect -> true, password -> mp)
val keys = sortParam(params) //> keys : scala.collection.SortedSet[String] = TreeSet(fullName, isBreakout,
//| meetingID, password, redirect)
val result = createBaseString(params) //> result : String = fullName=User+4621018&isBreakout=true&meetingID=random-1
//| 853792&password=mp&redirect=true
}

View File

@ -0,0 +1,126 @@
import scala.collection.immutable.StringOps
import org.bigbluebutton.core.apps.Answer
import org.bigbluebutton.core.apps.Question
object TestWorksheet {
println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet
val YesNoPollType = "YN" //> YesNoPollType : String = YN
val TrueFalsePollType = "TF" //> TrueFalsePollType : String = TF
val LetterPollType = "A-" //> LetterPollType : String = A-
val NumberPollType = "1-" //> NumberPollType : String = 1-
val LetterArray = Array("A", "B", "C", "D", "E", "F")
//> LetterArray : Array[String] = Array(A, B, C, D, E, F)
val NumberArray = Array("1", "2", "3", "4", "5", "6")
//> NumberArray : Array[String] = Array(1, 2, 3, 4, 5, 6)
def processYesNoPollType(qType: String):Question = {
val answers = new Array[Answer](2)
answers(0) = new Answer(0, "N", Some("No"))
answers(1) = new Answer(1, "Y", Some("Yes"))
new Question(0, YesNoPollType, false, None, answers)
} //> processYesNoPollType: (qType: String)org.bigbluebutton.core.apps.Question
def processTrueFalsePollType(qType: String):Question = {
val answers = new Array[Answer](2)
answers(0) = new Answer(0, "F", Some("False"))
answers(1) = new Answer(1, "T", Some("True"))
new Question(0, TrueFalsePollType, false, None, answers)
} //> processTrueFalsePollType: (qType: String)org.bigbluebutton.core.apps.Questio
//| n
def processLetterPollType(qType: String, multiResponse: Boolean):Option[Question] = {
val q = qType.split('-')
val numQs = q(1).toInt
var questionOption: Option[Question] = None
if (numQs > 0 && numQs <= 7) {
val answers = new Array[Answer](numQs)
var i = 0
for ( i <- 0 until numQs ) {
answers(i) = new Answer(i, LetterArray(i), Some(LetterArray(i)))
val question = new Question(0, LetterPollType, multiResponse, None, answers)
questionOption = Some(question)
}
}
questionOption
} //> processLetterPollType: (qType: String, multiResponse: Boolean)Option[org.bi
//| gbluebutton.core.apps.Question]
def processNumberPollType(qType: String, multiResponse: Boolean):Option[Question] = {
val q = qType.split('-')
val numQs = q(1).toInt
var questionOption: Option[Question] = None
if (numQs > 0 && numQs <= 7) {
val answers = new Array[Answer](numQs)
var i = 0
for ( i <- 0 until numQs ) {
answers(i) = new Answer(i, NumberArray(i), Some(NumberArray(i)))
val question = new Question(0, NumberPollType, multiResponse, None, answers)
questionOption = Some(question)
}
}
questionOption
} //> processNumberPollType: (qType: String, multiResponse: Boolean)Option[org.bi
//| gbluebutton.core.apps.Question]
def createQuestion(qType: String):Option[Question] = {
val qt = qType.toUpperCase()
var questionOption: Option[Question] = None
if (qt.matches(YesNoPollType)) {
questionOption = Some(processYesNoPollType(qt))
} else if (qt.matches(TrueFalsePollType)) {
questionOption = Some(processTrueFalsePollType(qt))
} else if (qt.startsWith(LetterPollType)) {
questionOption = processLetterPollType(qt, false)
} else if (qt.startsWith(NumberPollType)) {
processNumberPollType(qt, false)
} else {
questionOption = None
}
questionOption
} //> createQuestion: (qType: String)Option[org.bigbluebutton.core.apps.Question]
//|
def determineQType3(qType: String) {
val qt = qType.toUpperCase()
if (qt.matches(YesNoPollType)) {
println("YN")
} else if (qt.matches("TF")) {
println(TrueFalsePollType)
} else if (qt.startsWith(LetterPollType)) {
println("A5")
processLetterPollType(qt, false)
} else if (qt.startsWith(NumberPollType)) {
println("1")
processNumberPollType(qt, false)
} else {
println("No Match for [" + qType + "]")
}
} //> determineQType3: (qType: String)Unit
determineQType3("YN") //> YN
determineQType3("YF") //> No Match for [YF]
determineQType3("TF") //> TF
determineQType3("A-5") //> A5
determineQType3("1-5") //> 1
val list = new java.util.ArrayList[String]() //> list : java.util.ArrayList[String] = []
list.add("Red") //> res0: Boolean = true
list.add("Green") //> res1: Boolean = true
list.add("Blue") //> res2: Boolean = true
}

View File

@ -5,33 +5,30 @@ import org.bigbluebutton.common.messages.*;
public interface IBigBlueButtonInGW {
void handleJsonMessage(String json);
void handleBigBlueButtonMessage(IBigBlueButtonMessage message);
void isAliveAudit(String aliveID);
void statusMeetingAudit(String meetingID);
void endMeeting(String meetingID);
void endMeeting(String meetingId);
void endAllMeetings();
void createMeeting2(String meetingID, String externalMeetingID, String meetingName, boolean recorded,
String voiceBridge, long duration, boolean autoStartRecording,
boolean allowStartStopRecording, String moderatorPass, String viewerPass,
long createTime, String createDate);
void destroyMeeting(String meetingID);
void getAllMeetings(String meetingID);
void lockSettings(String meetingID, Boolean locked, Map<String, Boolean> lockSettigs);
// Polling
void votePoll(String meetingId, String userId, String pollId, Integer questionId, Integer answerId);
void startPoll(String meetingId, String requesterId, String pollId, String pollType);
void stopPoll(String meetingId, String userId, String pollId);
void showPollResult(String meetingId, String requesterId, String pollId, Boolean show);
// Lock
void initLockSettings(String meetingID, Map<String, Boolean> settings);
void sendLockSettings(String meetingID, String userId, Map<String, Boolean> settings);
void getLockSettings(String meetingId, String userId);
void lockUser(String meetingId, String requesterID, boolean lock, String internalUserID);
// Users
void validateAuthToken(String meetingId, String userId, String token, String correlationId, String sessionId);
void registerUser(String roomName, String userid, String username, String role, String externUserID, String authToken, String avatarURL);
@ -48,7 +45,7 @@ public interface IBigBlueButtonInGW {
void getRecordingStatus(String meetingId, String userId);
void userConnectedToGlobalAudio(String voiceConf, String userid, String name);
void userDisconnectedFromGlobalAudio(String voiceConf, String userid, String name);
// Voice
void initAudioSettings(String meetingID, String requesterID, Boolean muted);
void muteAllExceptPresenter(String meetingID, String requesterID, Boolean mute);
@ -66,7 +63,7 @@ public interface IBigBlueButtonInGW {
void voiceUserTalking(String meetingId, String userId, Boolean talking);
void voiceRecording(String meetingId, String recordingFile,
String timestamp, Boolean recording);
// Presentation
void clear(String meetingID);
void removePresentation(String meetingID, String presentationID);
@ -79,18 +76,18 @@ public interface IBigBlueButtonInGW {
void sendConversionUpdate(String messageKey, String meetingId,
String code, String presId, String presName);
void sendPageCountError(String messageKey, String meetingId,
String code, String presId, int numberOfPages,
int maxNumberPages, String presName);
void sendSlideGenerated(String messageKey, String meetingId,
String code, String presId, int numberOfPages,
int pagesCompleted, String presName);
void sendConversionCompleted(String messageKey, String meetingId,
String code, String presId, int numPages, String presName, String presBaseUrl);
// Layout
void getCurrentLayout(String meetingID, String requesterID);
void broadcastLayout(String meetingID, String requesterID, String layout);
@ -110,6 +107,11 @@ public interface IBigBlueButtonInGW {
void undoWhiteboard(String meetingID, String requesterID, String whiteboardId);
void enableWhiteboard(String meetingID, String requesterID, Boolean enable);
void isWhiteboardEnabled(String meetingID, String requesterID, String replyTo);
// Caption
void sendCaptionHistory(String meetingID, String requesterID);
void updateCaptionOwner(String meetingID, String locale, String ownerID);
void editCaptionHistory(String meetingID, String userID, Integer startIndex, Integer endIndex, String locale, String text);
// DeskShare
void deskShareStarted(String conferenceName, String callerId, String callerIdName);
@ -117,5 +119,4 @@ public interface IBigBlueButtonInGW {
void deskShareRTMPBroadcastStarted(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp);
void deskShareRTMPBroadcastStopped(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp);
void deskShareGetInfoRequest(String meetingId, String requesterId, String replyTo);
}

View File

@ -0,0 +1,44 @@
package org.bigbluebutton.core.pubsub.receivers;
import org.bigbluebutton.common.messages.MessagingConstants;
import org.bigbluebutton.common.messages.EditCaptionHistoryMessage;
import org.bigbluebutton.common.messages.SendCaptionHistoryRequestMessage;
import org.bigbluebutton.common.messages.UpdateCaptionOwnerMessage;
import com.google.gson.JsonParser;
import com.google.gson.JsonObject;
import org.bigbluebutton.core.api.IBigBlueButtonInGW;
public class CaptionMessageReceiver implements MessageHandler{
private IBigBlueButtonInGW bbbGW;
public CaptionMessageReceiver(IBigBlueButtonInGW bbbGW) {
this.bbbGW = bbbGW;
}
@Override
public void handleMessage(String pattern, String channel, String message) {
if (channel.equalsIgnoreCase(MessagingConstants.TO_CAPTION_CHANNEL)) {
JsonParser parser = new JsonParser();
JsonObject obj = (JsonObject) parser.parse(message);
if (obj.has("header") && obj.has("payload")) {
JsonObject header = (JsonObject) obj.get("header");
if (header.has("name")) {
String messageName = header.get("name").getAsString();
if (SendCaptionHistoryRequestMessage.SEND_CAPTION_HISTORY_REQUEST.equals(messageName)){
SendCaptionHistoryRequestMessage msg = SendCaptionHistoryRequestMessage.fromJson(message);
bbbGW.sendCaptionHistory(msg.meetingID, msg.requesterID);
} else if (UpdateCaptionOwnerMessage.UPDATE_CAPTION_OWNER.equals(messageName)) {
UpdateCaptionOwnerMessage msg = UpdateCaptionOwnerMessage.fromJson(message);
bbbGW.updateCaptionOwner(msg.meetingID, msg.locale, msg.ownerID);
} else if (EditCaptionHistoryMessage.EDIT_CAPTION_HISTORY.equals(messageName)) {
EditCaptionHistoryMessage msg = EditCaptionHistoryMessage.fromJson(message);
bbbGW.editCaptionHistory(msg.meetingID, msg.userID, msg.startIndex, msg.endIndex, msg.locale, msg.text);
}
}
}
}
}
}

View File

@ -3,7 +3,6 @@ package org.bigbluebutton.core.pubsub.receivers;
import java.util.HashMap;
import java.util.Map;
import org.bigbluebutton.common.messages.CreateMeetingMessage;
import org.bigbluebutton.common.messages.DestroyMeetingMessage;
import org.bigbluebutton.common.messages.EndMeetingMessage;
import org.bigbluebutton.common.messages.GetAllMeetingsRequest;
@ -17,6 +16,7 @@ import org.bigbluebutton.common.messages.UserConnectedToGlobalAudio;
import org.bigbluebutton.common.messages.UserDisconnectedFromGlobalAudio;
import org.bigbluebutton.common.messages.ValidateAuthTokenMessage;
import org.bigbluebutton.core.api.IBigBlueButtonInGW;
import org.bigbluebutton.messages.CreateMeetingRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -34,20 +34,30 @@ public class MeetingMessageReceiver implements MessageHandler {
}
public void handleMessage(String pattern, String channel, String message) {
// LOG.debug("Checking message: " + pattern + " " + channel + " " + message);
if (channel.equalsIgnoreCase(MessagingConstants.TO_MEETING_CHANNEL)) {
// System.out.println("Meeting message: " + channel + " " + message);
System.out.println("Meeting message: " + channel + " " + message);
JsonParser parser = new JsonParser();
JsonObject obj = (JsonObject) parser.parse(message);
if (obj.has("header") && obj.has("payload")) {
JsonObject header = (JsonObject) obj.get("header");
if (header.has("name")) {
String messageName = header.get("name").getAsString();
if (CreateMeetingRequest.NAME.equals(messageName)) {
Gson gson = new Gson();
CreateMeetingRequest msg = gson.fromJson(message,
CreateMeetingRequest.class);
bbbGW.handleBigBlueButtonMessage(msg);
}
}
}
IBigBlueButtonMessage msg = MessageFromJsonConverter.convert(message);
if (msg != null) {
if (msg instanceof EndMeetingMessage) {
EndMeetingMessage emm = (EndMeetingMessage) msg;
bbbGW.endMeeting(emm.meetingId);
} else if (msg instanceof CreateMeetingMessage) {
CreateMeetingMessage emm = (CreateMeetingMessage) msg;
bbbGW.createMeeting2(emm.id, emm.externalId, emm.name, emm.record, emm.voiceBridge,
emm.duration, emm.autoStartRecording, emm.allowStartStopRecording,
emm.moderatorPass, emm.viewerPass, emm.createTime, emm.createDate);
} else if (msg instanceof RegisterUserMessage) {
RegisterUserMessage emm = (RegisterUserMessage) msg;
bbbGW.registerUser(emm.meetingID, emm.internalUserId, emm.fullname, emm.role, emm.externUserID, emm.authToken, emm.avatarURL);

View File

@ -138,8 +138,7 @@ public class PresentationMessageListener implements MessageHandler {
}
}
else {
Gson gson = new Gson();
HashMap<String,String> map = gson.fromJson(message, new TypeToken<Map<String, String>>() {}.getType());
HashMap<String,String> map = new Gson().fromJson(message, new TypeToken<HashMap<String, String>>() {}.getType());
String code = (String) map.get("returnCode");
String presId = (String) map.get("presentationId");
String filename = (String) map.get("filename");

View File

@ -54,6 +54,9 @@ public class RedisMessageReceiver {
MeetingMessageReceiver meetingRx = new MeetingMessageReceiver(bbbGW);
receivers.add(meetingRx);
CaptionMessageReceiver captionRx = new CaptionMessageReceiver(bbbGW);
receivers.add(captionRx);
}
public void handleMessage(String pattern, String channel, String message) {

View File

@ -1,40 +1,17 @@
package org.bigbluebutton.core.pubsub.receivers;
import org.bigbluebutton.common.messages.AssignPresenterRequestMessage;
import org.bigbluebutton.common.messages.BroadcastLayoutRequestMessage;
import org.bigbluebutton.common.messages.EjectUserFromMeetingRequestMessage;
import org.bigbluebutton.common.messages.EjectUserFromVoiceRequestMessage;
import org.bigbluebutton.common.messages.GetCurrentLayoutRequestMessage;
import org.bigbluebutton.common.messages.GetRecordingStatusRequestMessage;
import org.bigbluebutton.common.messages.GetUsersRequestMessage;
import org.bigbluebutton.common.messages.InitAudioSettingsMessage;
import org.bigbluebutton.common.messages.InitPermissionsSettingMessage;
import org.bigbluebutton.common.messages.IsMeetingMutedRequestMessage;
import org.bigbluebutton.common.messages.LockLayoutRequestMessage;
import org.bigbluebutton.common.messages.LockMuteUserRequestMessage;
import org.bigbluebutton.common.messages.MessagingConstants;
import org.bigbluebutton.common.messages.MuteAllExceptPresenterRequestMessage;
import org.bigbluebutton.common.messages.MuteAllRequestMessage;
import org.bigbluebutton.common.messages.MuteUserRequestMessage;
import org.bigbluebutton.common.messages.SetRecordingStatusRequestMessage;
import org.bigbluebutton.common.messages.SetUserStatusRequestMessage;
import org.bigbluebutton.common.messages.UserJoinedVoiceConfMessage;
import org.bigbluebutton.common.messages.UserLeavingMessage;
import org.bigbluebutton.common.messages.UserLeftVoiceConfMessage;
import org.bigbluebutton.common.messages.UserLockedInVoiceConfMessage;
import org.bigbluebutton.common.messages.UserMutedInVoiceConfMessage;
import org.bigbluebutton.common.messages.UserEmojiStatusMessage;
import org.bigbluebutton.common.messages.UserShareWebcamRequestMessage;
import org.bigbluebutton.common.messages.UserTalkingInVoiceConfMessage;
import org.bigbluebutton.common.messages.UserUnshareWebcamRequestMessage;
import org.bigbluebutton.common.messages.VoiceConfRecordingStartedMessage;
import org.bigbluebutton.common.messages.*;
import org.bigbluebutton.core.api.IBigBlueButtonInGW;
import org.bigbluebutton.messages.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonParser;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class UsersMessageReceiver implements MessageHandler{
private static final Logger LOG = LoggerFactory.getLogger(UsersMessageReceiver.class);
private IBigBlueButtonInGW bbbInGW;
@ -45,7 +22,6 @@ public class UsersMessageReceiver implements MessageHandler{
@Override
public void handleMessage(String pattern, String channel, String message) {
if (channel.equalsIgnoreCase(MessagingConstants.TO_USERS_CHANNEL)) {
// System.out.println("Users message: " + channel + " " + message);
JsonParser parser = new JsonParser();
JsonObject obj = (JsonObject) parser.parse(message);
if (obj.has("header") && obj.has("payload")) {
@ -77,7 +53,7 @@ public class UsersMessageReceiver implements MessageHandler{
break;
case SetRecordingStatusRequestMessage.SET_RECORDING_STATUS_REQUEST:
processSetRecordingStatusRequestMessage(message);
break;
break;
case GetRecordingStatusRequestMessage.GET_RECORDING_STATUS_REQUEST:
processGetRecordingStatusRequestMessage(message);
break;
@ -117,7 +93,21 @@ public class UsersMessageReceiver implements MessageHandler{
case EjectUserFromVoiceRequestMessage.EJECT_USER_FROM_VOICE_REQUEST:
processEjectUserFromVoiceRequestMessage(message);
break;
case GetBreakoutRoomsList.NAME:
bbbInGW.handleJsonMessage(message);
break;
case CreateBreakoutRoomsRequest.NAME:
bbbInGW.handleJsonMessage(message);
break;
case ListenInOnBreakout.NAME:
bbbInGW.handleJsonMessage(message);
break;
case RequestBreakoutJoinURL.NAME:
bbbInGW.handleJsonMessage(message);
break;
case EndAllBreakoutRoomsRequest.NAME:
bbbInGW.handleJsonMessage(message);
break;
}
}
}
@ -341,6 +331,6 @@ public class UsersMessageReceiver implements MessageHandler{
EjectUserFromVoiceRequestMessage msg = EjectUserFromVoiceRequestMessage.fromJson(message);
if (msg != null) {
bbbInGW.ejectUserFromVoice(msg.meetingId, msg.userId, msg.requesterId);
}
}
}
}

View File

@ -0,0 +1,28 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.core.recorders.events;
import org.bigbluebutton.core.service.recorder.RecordEvent;
public abstract class AbstractCaptionRecordEvent extends RecordEvent {
public AbstractCaptionRecordEvent() {
setModule("CAPTION");
}
}

View File

@ -0,0 +1,48 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.core.recorders.events;
public class EditCaptionHistoryRecordEvent extends AbstractCaptionRecordEvent {
private static final String START_INDEX = "startIndex";
private static final String END_INDEX = "endIndex";
private static final String LOCALE = "locale";
private static final String TEXT = "text";
public EditCaptionHistoryRecordEvent() {
super();
setEvent("EditCaptionHistoryEvent");
}
public void setStartIndex(String startIndex) {
eventMap.put(START_INDEX, startIndex);
}
public void setEndIndex(String endIndex) {
eventMap.put(END_INDEX, endIndex);
}
public void setLocale(String locale) {
eventMap.put(LOCALE, locale);
}
public void setText(String text) {
eventMap.put(TEXT, text);
}
}

View File

@ -0,0 +1,96 @@
package org.bigbluebutton.core.util;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
public class WebApiUtil {
private final String securitySalt = "changeme";
//
//checksum() -- Return a checksum based on SHA-1 digest
//
public String checksum(String s) {
String checksum = "";
try {
checksum = DigestUtils.shaHex(s);
} catch (Exception e) {
e.printStackTrace();
}
return checksum;
}
//From the list of parameters we want to pass. Creates a base string with parameters
//sorted in alphabetical order for us to sign.
public String createBaseString(Map<String, String[]> params) {
StringBuffer csbuf = new StringBuffer();
SortedSet<String> keys = new TreeSet<String>(params.keySet());
boolean first = true;
for (String key: keys) {
for (String value: params.get(key)) {
if (first) {
first = false;
} else {
csbuf.append("&");
}
csbuf.append(key);
csbuf.append("=");
csbuf.append(value);
}
}
return csbuf.toString();
}
public boolean isChecksumSame(String apiCall, String checksum, String queryString) {
if (StringUtils.isEmpty(securitySalt)) {
return true;
}
if( queryString == null ) {
queryString = "";
} else {
// handle either checksum as first or middle / end parameter
// TODO: this is hackish - should be done better
queryString = queryString.replace("&checksum=" + checksum, "");
queryString = queryString.replace("checksum=" + checksum + "&", "");
queryString = queryString.replace("checksum=" + checksum, "");
}
String cs = DigestUtils.shaHex(apiCall + queryString + securitySalt);
if (cs == null || cs.equals(checksum) == false) {
return false;
}
return true;
}
//
//encodeURIComponent() -- Java encoding similiar to JavaScript encodeURIComponent
//
public String encodeURIComponent(String component) {
String result = null;
try {
result = URLEncoder.encode(component, "UTF-8")
.replaceAll("\\%28", "(")
.replaceAll("\\%29", ")")
.replaceAll("\\+", "%20")
.replaceAll("\\%27", "'")
.replaceAll("\\%21", "!")
.replaceAll("\\%7E", "~");
} catch (UnsupportedEncodingException e) {
result = component;
}
return result;
}
}

View File

@ -33,8 +33,23 @@ redis {
password=""
# recording keys should expire in 14 days
keyExpiry=1209600
}
http {
interface = "0.0.0.0"
port = 9000
}
services {
bbbWebHost = "localhost"
bbbWebPort = 88888
bbbWebAPI = "http://192.168.23.33/bigbluebutton/api"
sharedSecret = "changeme"
moderatorPassword = "mp"
viewerPassword = "ap"
defaultPresentationURL = "http://localhost/default.pdf"
}
red5 {
deskshareip="192.168.0.109"
deskshareapp="video-broadcast"

View File

@ -1,10 +1,10 @@
package org.bigbluebutton
import akka.event.{ LoggingAdapter, Logging }
import akka.actor.{ ActorSystem, Props }
import scala.concurrent.duration._
import redis.RedisClient
import scala.concurrent.{ Future, Await }
import scala.concurrent.ExecutionContext.Implicits.global
import org.bigbluebutton.endpoint.redis.RedisPublisher
import org.bigbluebutton.endpoint.redis.KeepAliveRedisPublisher
import org.bigbluebutton.endpoint.redis.AppsRedisSubscriberActor
@ -12,15 +12,27 @@ import org.bigbluebutton.core.api.MessageOutGateway
import org.bigbluebutton.core.api.IBigBlueButtonInGW
import org.bigbluebutton.core.BigBlueButtonInGW
import org.bigbluebutton.core.MessageSender
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.MessageSenderActor
import org.bigbluebutton.core.RecorderActor
import org.bigbluebutton.core.pubsub.receivers.RedisMessageReceiver
import org.bigbluebutton.core.api.OutMessageListener2
import org.bigbluebutton.core.pubsub.senders._
import org.bigbluebutton.core.service.recorder.RedisDispatcher
import org.bigbluebutton.core.service.recorder.RecorderApplication
import org.bigbluebutton.core.bus._
import org.bigbluebutton.core.JsonMessageSenderActor
object Boot extends App with SystemConfiguration {
implicit val system = ActorSystem("bigbluebutton-apps-system")
implicit val executor = system.dispatcher
val logger = Logging(system, getClass)
val eventBus = new IncomingEventBus
val outgoingEventBus = new OutgoingEventBus
val outGW = new OutMessageGateway(outgoingEventBus)
val redisPublisher = new RedisPublisher(system)
val msgSender = new MessageSender(redisPublisher)
@ -29,7 +41,15 @@ object Boot extends App with SystemConfiguration {
val recorderApp = new RecorderApplication(redisDispatcher)
recorderApp.start()
val bbbInGW = new BigBlueButtonInGW(system, recorderApp, msgSender, red5DeskShareIP, red5DeskShareApp)
val messageSenderActor = system.actorOf(MessageSenderActor.props(msgSender), "messageSenderActor")
val recorderActor = system.actorOf(RecorderActor.props(recorderApp), "recorderActor")
val newMessageSenderActor = system.actorOf(JsonMessageSenderActor.props(msgSender), "newMessageSenderActor")
outgoingEventBus.subscribe(messageSenderActor, "outgoingMessageChannel")
outgoingEventBus.subscribe(recorderActor, "outgoingMessageChannel")
outgoingEventBus.subscribe(newMessageSenderActor, "outgoingMessageChannel")
val bbbInGW = new BigBlueButtonInGW(system, eventBus, outGW, red5DeskShareIP, red5DeskShareApp)
val redisMsgReceiver = new RedisMessageReceiver(bbbInGW)
val redisSubscriberActor = system.actorOf(AppsRedisSubscriberActor.props(redisMsgReceiver), "redis-subscriber")

View File

@ -10,7 +10,16 @@ trait SystemConfiguration {
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("")
lazy val httpInterface = Try(config.getString("http.interface")).getOrElse("")
lazy val httpPort = Try(config.getInt("http.port")).getOrElse(9090)
lazy val bbbWebHost = Try(config.getString("services.bbbWebHost")).getOrElse("localhost")
lazy val bbbWebPort = Try(config.getInt("services.bbbWebPort")).getOrElse(8888)
lazy val bbbWebAPI = Try(config.getString("services.bbbWebAPI")).getOrElse("localhost")
lazy val bbbWebSharedSecret = Try(config.getString("services.sharedSecret")).getOrElse("changeme")
lazy val bbbWebModeratorPassword = Try(config.getString("services.moderatorPassword")).getOrElse("changeme")
lazy val bbbWebViewerPassword = Try(config.getString("services.viewerPassword")).getOrElse("changeme")
lazy val bbbWebDefaultPresentationURL = Try(config.getString("services.defaultPresentationURL")).getOrElse("changeme")
lazy val keysExpiresInSec = Try(config.getInt("redis.keyExpiry")).getOrElse(14 * 86400) // 14 days
lazy val red5DeskShareIP = Try(config.getString("red5.deskshareip")).getOrElse("127.0.0.1")
lazy val red5DeskShareApp = Try(config.getString("red5.deskshareapp")).getOrElse("")
}
}

View File

@ -5,34 +5,24 @@ import akka.actor.ActorLogging
import akka.pattern.{ ask, pipe }
import akka.util.Timeout
import scala.concurrent.duration._
import scala.collection.mutable.HashMap
import org.bigbluebutton.core.bus._
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.util._
import org.bigbluebutton.core.api.ValidateAuthTokenTimedOut
import scala.util.Success
import scala.util.Failure
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.core.recorders.events.VoiceUserJoinedRecordEvent
import org.bigbluebutton.core.recorders.events.VoiceUserLeftRecordEvent
import org.bigbluebutton.core.recorders.events.VoiceUserLockedRecordEvent
import org.bigbluebutton.core.recorders.events.VoiceUserMutedRecordEvent
import org.bigbluebutton.core.recorders.events.VoiceStartRecordingRecordEvent
import org.bigbluebutton.core.recorders.events.VoiceUserTalkingRecordEvent
import org.bigbluebutton.core.service.recorder.RecorderApplication
import scala.collection._
import com.google.gson.Gson
object BigBlueButtonActor extends SystemConfiguration {
def props(system: ActorSystem, recorderApp: RecorderApplication, messageSender: MessageSender): Props =
Props(classOf[BigBlueButtonActor], system, recorderApp, messageSender)
def props(system: ActorSystem,
eventBus: IncomingEventBus,
outGW: OutMessageGateway): Props =
Props(classOf[BigBlueButtonActor], system, eventBus, outGW)
}
class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplication, messageSender: MessageSender) extends Actor with ActorLogging {
class BigBlueButtonActor(val system: ActorSystem,
eventBus: IncomingEventBus, outGW: OutMessageGateway) extends Actor with ActorLogging {
implicit def executionContext = system.dispatcher
implicit val timeout = Timeout(5 seconds)
private var meetings = new collection.immutable.HashMap[String, RunningMeeting]
private val outGW = new OutMessageGateway("bbbActorOutGW", recorderApp, messageSender)
def receive = {
case msg: CreateMeeting => handleCreateMeeting(msg)
@ -52,21 +42,15 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
case msg: DeskShareRTMPBroadcastStartedRequest => handleDeskShareRTMPBroadcastStartedRequest(msg)
case msg: DeskShareRTMPBroadcastStoppedRequest => handleDeskShareRTMPBroadcastStoppedRequest(msg)
case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
case msg: InMessage => handleMeetingMessage(msg)
case _ => // do nothing
}
private def findMeetingWithVoiceConfId(voiceConfId: String): Option[RunningMeeting] = {
meetings.values.find(m => {
println("+++ compare " + m.mProps.voiceBridge + " with our " + voiceConfId)
m.mProps.voiceBridge == voiceConfId
})
meetings.values.find(m => { m.mProps.voiceBridge == voiceConfId })
}
private def handleUserJoinedVoiceConfMessage(msg: UserJoinedVoiceConfMessage) {
findMeetingWithVoiceConfId(msg.voiceConfId) foreach { m =>
m.actorRef ! msg
}
findMeetingWithVoiceConfId(msg.voiceConfId) foreach { m => m.actorRef ! msg }
}
private def handleUserLeftVoiceConfMessage(msg: UserLeftVoiceConfMessage) {
@ -98,62 +82,27 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
findMeetingWithVoiceConfId(msg.voiceConfId) foreach { m =>
m.actorRef ! msg
}
}
private def handleValidateAuthToken(msg: ValidateAuthToken) {
meetings.get(msg.meetingID) foreach { m =>
val future = m.actorRef.ask(msg)(5 seconds)
m.actorRef ! msg
future onComplete {
case Success(result) => {
log.info("Validate auth token response. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
/**
* Received a reply from MeetingActor which means hasn't hung!
* Sometimes, the actor seems to hang and doesn't anymore accept messages. This is a simple
* audit to check whether the actor is still alive. (ralam feb 25, 2015)
*/
}
case Failure(failure) => {
log.warning("Validate auth token timeout. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
outGW.send(new ValidateAuthTokenTimedOut(msg.meetingID, msg.userId, msg.token, false, msg.correlationId))
}
}
}
}
private def handleMeetingMessage(msg: InMessage): Unit = {
msg match {
case ucm: UserConnectedToGlobalAudio => {
val m = meetings.values.find(m => m.mProps.voiceBridge == ucm.voiceConf)
m foreach { mActor => mActor.actorRef ! ucm }
}
case udm: UserDisconnectedFromGlobalAudio => {
val m = meetings.values.find(m => m.mProps.voiceBridge == udm.voiceConf)
m foreach { mActor => mActor.actorRef ! udm }
}
case allOthers => {
meetings.get(allOthers.meetingID) match {
case None => handleMeetingNotFound(allOthers)
case Some(m) => {
// log.debug("Forwarding message [{}] to meeting [{}]", msg.meetingID)
m.actorRef ! allOthers
}
}
}
}
}
private def handleMeetingNotFound(msg: InMessage) {
msg match {
case vat: ValidateAuthToken => {
log.info("No meeting [" + vat.meetingID + "] for auth token [" + vat.token + "]")
outGW.send(new ValidateAuthTokenReply(vat.meetingID, vat.userId, vat.token, false, vat.correlationId))
}
case _ => {
log.info("No meeting [" + msg.meetingID + "] for message type [" + msg.getClass() + "]")
// do nothing
}
// val future = m.actorRef.ask(msg)(5 seconds)
// future onComplete {
// case Success(result) => {
// log.info("Validate auth token response. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
// /**
// * Received a reply from MeetingActor which means hasn't hung!
// * Sometimes, the actor seems to hang and doesn't anymore accept messages. This is a simple
// * audit to check whether the actor is still alive. (ralam feb 25, 2015)
// */
// }
// case Failure(failure) => {
// log.warning("Validate auth token timeout. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
// outGW.send(new ValidateAuthTokenTimedOut(msg.meetingID, msg.userId, msg.token, false, msg.correlationId))
// }
// }
}
}
@ -162,7 +111,6 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
}
private def handlePubSubPingMessage(msg: PubSubPing): Unit = {
//log.info("PubSubPing from [" + msg.system + "]")
outGW.send(new PubSubPong(msg.system, msg.timestamp))
}
@ -173,11 +121,20 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
case Some(m) => {
meetings -= msg.meetingID
log.info("Kick everyone out on meetingId={}", msg.meetingID)
if (m.mProps.isBreakout) {
log.info("Informing parent meeting {} that a breakout room has been ended{}", m.mProps.externalMeetingID, m.mProps.meetingID)
eventBus.publish(BigBlueButtonEvent(m.mProps.externalMeetingID,
BreakoutRoomEnded(m.mProps.externalMeetingID, m.mProps.meetingID)))
}
outGW.send(new EndAndKickAll(msg.meetingID, m.mProps.recorded))
outGW.send(new DisconnectAllUsers(msg.meetingID))
log.info("Destroyed meetingId={}", msg.meetingID)
outGW.send(new MeetingDestroyed(msg.meetingID))
/** Unsubscribe to meeting and voice events. **/
eventBus.unsubscribe(m.actorRef, m.mProps.meetingID)
eventBus.unsubscribe(m.actorRef, m.mProps.voiceBridge)
// Stop the meeting actor.
context.stop(m.actorRef)
}
@ -188,8 +145,12 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
meetings.get(msg.meetingID) match {
case None => {
log.info("Create meeting request. meetingId={}", msg.mProps.meetingID)
val moutGW = new OutMessageGateway("meetingOutGW-" + msg.meetingID, recorderApp, messageSender)
var m = RunningMeeting(msg.mProps, moutGW)
var m = RunningMeeting(msg.mProps, outGW, eventBus)
/** Subscribe to meeting and voice events. **/
eventBus.subscribe(m.actorRef, m.mProps.meetingID)
eventBus.subscribe(m.actorRef, m.mProps.voiceBridge)
meetings += m.mProps.meetingID -> m
outGW.send(new MeetingCreated(m.mProps.meetingID, m.mProps.externalMeetingID, m.mProps.recorded, m.mProps.meetingName,
@ -197,7 +158,6 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
msg.mProps.viewerPass, msg.mProps.createTime, msg.mProps.createDate))
m.actorRef ! new InitializeMeeting(m.mProps.meetingID, m.mProps.recorded)
m.actorRef ! "StartTimer"
}
case Some(m) => {
log.info("Meeting already created. meetingID={}", msg.mProps.meetingID)
@ -207,42 +167,41 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
}
private def handleGetAllMeetingsRequest(msg: GetAllMeetingsRequest) {
val len = meetings.keys.size
var currPosition = len - 1
var resultArray: Array[MeetingInfo] = new Array[MeetingInfo](len)
var len = meetings.keys.size
println("meetings.size=" + meetings.size)
println("len_=" + len)
meetings.values.foreach(m => {
val id = m.mProps.meetingID
val duration = m.mProps.duration
val name = m.mProps.meetingName
val recorded = m.mProps.recorded
val voiceBridge = m.mProps.voiceBridge
val set = meetings.keySet
val arr: Array[String] = new Array[String](len)
set.copyToArray(arr)
val resultArray: Array[MeetingInfo] = new Array[MeetingInfo](len)
val info = new MeetingInfo(id, name, recorded, voiceBridge, duration)
resultArray(currPosition) = info
currPosition = currPosition - 1
for (i <- 0 until arr.length) {
val id = arr(i)
val duration = meetings.get(arr(i)).head.mProps.duration
val name = meetings.get(arr(i)).head.mProps.meetingName
val recorded = meetings.get(arr(i)).head.mProps.recorded
val voiceBridge = meetings.get(arr(i)).head.mProps.voiceBridge
var info = new MeetingInfo(id, name, recorded, voiceBridge, duration)
resultArray(i) = info
val html5clientRequesterID = "nodeJSapp"
//send the users
self ! (new GetUsers(id, "nodeJSapp"))
eventBus.publish(BigBlueButtonEvent(id, new GetUsers(id, html5clientRequesterID)))
//send the presentation
self ! (new GetPresentationInfo(id, "nodeJSapp", "nodeJSapp"))
eventBus.publish(BigBlueButtonEvent(id, new GetPresentationInfo(id, html5clientRequesterID, html5clientRequesterID)))
//send chat history
self ! (new GetChatHistoryRequest(id, "nodeJSapp", "nodeJSapp"))
eventBus.publish(BigBlueButtonEvent(id, new GetChatHistoryRequest(id, html5clientRequesterID, html5clientRequesterID)))
//send lock settings
self ! (new GetLockSettings(id, "nodeJSapp"))
eventBus.publish(BigBlueButtonEvent(id, new GetLockSettings(id, html5clientRequesterID)))
//send desktop sharing info
self ! (new DeskShareGetDeskShareInfoRequest(id, "nodeJSapp", "nodeJSapp"))
eventBus.publish(BigBlueButtonEvent(id, new DeskShareGetDeskShareInfoRequest(id, html5clientRequesterID, html5clientRequesterID)))
}
// send captions
eventBus.publish(BigBlueButtonEvent(id, new SendCaptionHistoryRequest(id, html5clientRequesterID)))
})
outGW.send(new GetAllMeetingsReply(resultArray))
}

View File

@ -1,5 +1,6 @@
package org.bigbluebutton.core
import org.bigbluebutton.core.bus._
import org.bigbluebutton.core.api._
import scala.collection.JavaConversions._
import java.util.ArrayList
@ -17,59 +18,108 @@ import org.bigbluebutton.core.service.recorder.RecorderApplication
import org.bigbluebutton.common.messages.IBigBlueButtonMessage
import org.bigbluebutton.common.messages.StartCustomPollRequestMessage
import org.bigbluebutton.common.messages.PubSubPingMessage
import org.bigbluebutton.messages._
import org.bigbluebutton.messages.payload._
import akka.event.Logging
import spray.json.JsonParser
class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplication, messageSender: MessageSender,
val red5DeskShareIP: String, val red5DeskShareApp: String) extends IBigBlueButtonInGW {
class BigBlueButtonInGW(
val system: ActorSystem,
eventBus: IncomingEventBus,
outGW: OutMessageGateway,
val red5DeskShareIP: String,
val red5DeskShareApp: String) extends IBigBlueButtonInGW {
val log = system.log
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, recorderApp, messageSender), "bigbluebutton-actor")
val log = Logging(system, getClass)
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, eventBus, outGW), "bigbluebutton-actor")
eventBus.subscribe(bbbActor, "meeting-manager")
def handleBigBlueButtonMessage(message: IBigBlueButtonMessage) {
message match {
case msg: StartCustomPollRequestMessage => {
bbbActor ! new StartCustomPollRequest(msg.payload.meetingId, msg.payload.requesterId, msg.payload.pollType, msg.payload.answers)
eventBus.publish(
BigBlueButtonEvent(
"meeting-manager",
new StartCustomPollRequest(
msg.payload.meetingId,
msg.payload.requesterId,
msg.payload.pollType,
msg.payload.answers)))
}
case msg: PubSubPingMessage => {
bbbActor ! new PubSubPing(msg.payload.system, msg.payload.timestamp)
eventBus.publish(
BigBlueButtonEvent(
"meeting-manager",
new PubSubPing(msg.payload.system, msg.payload.timestamp)))
}
case msg: CreateMeetingRequest => {
val mProps = new MeetingProperties(
msg.payload.id,
msg.payload.externalId,
msg.payload.name,
msg.payload.record,
msg.payload.voiceConfId,
msg.payload.durationInMinutes,
msg.payload.autoStartRecording,
msg.payload.allowStartStopRecording,
msg.payload.moderatorPassword,
msg.payload.viewerPassword,
msg.payload.createTime,
msg.payload.createDate,
red5DeskShareIP, red5DeskShareApp,
msg.payload.isBreakout)
eventBus.publish(BigBlueButtonEvent("meeting-manager", new CreateMeeting(msg.payload.id, mProps)))
}
}
}
// Meeting
def createMeeting2(meetingID: String, externalMeetingID: String, meetingName: String, record: Boolean,
voiceBridge: String, duration: Long, autoStartRecording: Boolean,
allowStartStopRecording: Boolean, moderatorPass: String, viewerPass: String,
createTime: Long, createDate: String) {
def handleJsonMessage(json: String) {
JsonMessageDecoder.decode(json) match {
case Some(validMsg) => forwardMessage(validMsg)
case None => log.error("Unhandled json message: {}", json)
}
}
val mProps = new MeetingProperties(meetingID, externalMeetingID, meetingName, record,
voiceBridge, duration, autoStartRecording, allowStartStopRecording,
moderatorPass, viewerPass, createTime, createDate, red5DeskShareIP, red5DeskShareApp)
bbbActor ! new CreateMeeting(meetingID, mProps)
def forwardMessage(msg: InMessage) = {
msg match {
case m: BreakoutRoomsListMessage => eventBus.publish(BigBlueButtonEvent(m.meetingId, m))
case m: CreateBreakoutRooms => eventBus.publish(BigBlueButtonEvent(m.meetingId, m))
case m: RequestBreakoutJoinURLInMessage => eventBus.publish(BigBlueButtonEvent(m.meetingId, m))
case m: TransferUserToMeetingRequest => eventBus.publish(BigBlueButtonEvent(m.meetingId, m))
case m: EndAllBreakoutRooms => eventBus.publish(BigBlueButtonEvent(m.meetingId, m))
case _ => log.error("Unhandled message: {}", msg)
}
}
def destroyMeeting(meetingID: String) {
bbbActor ! new DestroyMeeting(meetingID)
forwardMessage(new EndAllBreakoutRooms(meetingID))
eventBus.publish(
BigBlueButtonEvent(
"meeting-manager",
new DestroyMeeting(
meetingID)))
}
def getAllMeetings(meetingID: String) {
bbbActor ! new GetAllMeetingsRequest("meetingId")
eventBus.publish(BigBlueButtonEvent("meeting-manager", new GetAllMeetingsRequest("meetingId")))
}
def isAliveAudit(aliveId: String) {
bbbActor ! new KeepAliveMessage(aliveId)
eventBus.publish(BigBlueButtonEvent("meeting-manager", new KeepAliveMessage(aliveId)))
}
def lockSettings(meetingID: String, locked: java.lang.Boolean,
lockSettings: java.util.Map[String, java.lang.Boolean]) {
}
def statusMeetingAudit(meetingID: String) {
}
def endMeeting(meetingID: String) {
bbbActor ! new EndMeeting(meetingID)
def endMeeting(meetingId: String) {
eventBus.publish(BigBlueButtonEvent(meetingId, new EndMeeting(meetingId)))
}
def endAllMeetings() {
@ -82,12 +132,12 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
* ***********************************************************
*/
def validateAuthToken(meetingId: String, userId: String, token: String, correlationId: String, sessionId: String) {
bbbActor ! new ValidateAuthToken(meetingId, userId, token, correlationId, sessionId)
eventBus.publish(BigBlueButtonEvent(meetingId, new ValidateAuthToken(meetingId, userId, token, correlationId, sessionId)))
}
def registerUser(meetingID: String, userID: String, name: String, role: String, extUserID: String, authToken: String, avatarURL: String): Unit = {
val userRole = if (role == "MODERATOR") Role.MODERATOR else Role.VIEWER
bbbActor ! new RegisterUser(meetingID, userID, name, userRole, extUserID, authToken, avatarURL)
eventBus.publish(BigBlueButtonEvent(meetingID, new RegisterUser(meetingID, userID, name, userRole, extUserID, authToken, avatarURL)))
}
def sendLockSettings(meetingID: String, userId: String, settings: java.util.Map[String, java.lang.Boolean]) {
@ -112,7 +162,7 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
lockOnJoin = lockOnJoin,
lockOnJoinConfigurable = lockOnJoinConfigurable)
bbbActor ! new SetLockSettings(meetingID, userId, permissions)
eventBus.publish(BigBlueButtonEvent(meetingID, new SetLockSettings(meetingID, userId, permissions)))
}
def initLockSettings(meetingID: String, settings: java.util.Map[String, java.lang.Boolean]) {
@ -136,64 +186,64 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
lockOnJoin = lockOnJoin,
lockOnJoinConfigurable = lockOnJoinConfigurable)
bbbActor ! new InitLockSettings(meetingID, permissions)
eventBus.publish(BigBlueButtonEvent(meetingID, new InitLockSettings(meetingID, permissions)))
}
def initAudioSettings(meetingID: String, requesterID: String, muted: java.lang.Boolean) {
bbbActor ! new InitAudioSettings(meetingID, requesterID, muted.booleanValue())
eventBus.publish(BigBlueButtonEvent(meetingID, new InitAudioSettings(meetingID, requesterID, muted.booleanValue())))
}
def getLockSettings(meetingId: String, userId: String) {
bbbActor ! new GetLockSettings(meetingId, userId)
eventBus.publish(BigBlueButtonEvent(meetingId, new GetLockSettings(meetingId, userId)))
}
def lockUser(meetingId: String, requesterID: String, lock: Boolean, userId: String) {
bbbActor ! new LockUserRequest(meetingId, requesterID, userId, lock)
eventBus.publish(BigBlueButtonEvent(meetingId, new LockUserRequest(meetingId, requesterID, userId, lock)))
}
def setRecordingStatus(meetingId: String, userId: String, recording: java.lang.Boolean) {
bbbActor ! new SetRecordingStatus(meetingId, userId, recording.booleanValue())
eventBus.publish(BigBlueButtonEvent(meetingId, new SetRecordingStatus(meetingId, userId, recording.booleanValue())))
}
def getRecordingStatus(meetingId: String, userId: String) {
bbbActor ! new GetRecordingStatus(meetingId, userId)
eventBus.publish(BigBlueButtonEvent(meetingId, new GetRecordingStatus(meetingId, userId)))
}
// Users
def userEmojiStatus(meetingId: String, userId: String, emojiStatus: String) {
bbbActor ! new UserEmojiStatus(meetingId, userId, emojiStatus)
eventBus.publish(BigBlueButtonEvent(meetingId, new UserEmojiStatus(meetingId, userId, emojiStatus)))
}
def ejectUserFromMeeting(meetingId: String, userId: String, ejectedBy: String) {
bbbActor ! new EjectUserFromMeeting(meetingId, userId, ejectedBy)
eventBus.publish(BigBlueButtonEvent(meetingId, new EjectUserFromMeeting(meetingId, userId, ejectedBy)))
}
def shareWebcam(meetingId: String, userId: String, stream: String) {
bbbActor ! new UserShareWebcam(meetingId, userId, stream)
eventBus.publish(BigBlueButtonEvent(meetingId, new UserShareWebcam(meetingId, userId, stream)))
}
def unshareWebcam(meetingId: String, userId: String, stream: String) {
bbbActor ! new UserUnshareWebcam(meetingId, userId, stream)
eventBus.publish(BigBlueButtonEvent(meetingId, new UserUnshareWebcam(meetingId, userId, stream)))
}
def setUserStatus(meetingID: String, userID: String, status: String, value: Object) {
bbbActor ! new ChangeUserStatus(meetingID, userID, status, value)
eventBus.publish(BigBlueButtonEvent(meetingID, new ChangeUserStatus(meetingID, userID, status, value)))
}
def getUsers(meetingID: String, requesterID: String) {
bbbActor ! new GetUsers(meetingID, requesterID)
eventBus.publish(BigBlueButtonEvent(meetingID, new GetUsers(meetingID, requesterID)))
}
def userLeft(meetingID: String, userID: String, sessionId: String): Unit = {
bbbActor ! new UserLeaving(meetingID, userID, sessionId)
eventBus.publish(BigBlueButtonEvent(meetingID, new UserLeaving(meetingID, userID, sessionId)))
}
def userJoin(meetingID: String, userID: String, authToken: String): Unit = {
bbbActor ! new UserJoining(meetingID, userID, authToken)
eventBus.publish(BigBlueButtonEvent(meetingID, new UserJoining(meetingID, userID, authToken)))
}
def assignPresenter(meetingID: String, newPresenterID: String, newPresenterName: String, assignedBy: String): Unit = {
bbbActor ! new AssignPresenter(meetingID, newPresenterID, newPresenterName, assignedBy)
eventBus.publish(BigBlueButtonEvent(meetingID, new AssignPresenter(meetingID, newPresenterID, newPresenterName, assignedBy)))
}
def getCurrentPresenter(meetingID: String, requesterID: String): Unit = {
@ -203,13 +253,13 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
def userConnectedToGlobalAudio(voiceConf: String, userid: String, name: String) {
// we are required to pass the meeting_id as first parameter (just to satisfy trait)
// but it's not used anywhere. That's why we pass voiceConf twice instead
bbbActor ! new UserConnectedToGlobalAudio(voiceConf, voiceConf, userid, name)
eventBus.publish(BigBlueButtonEvent(voiceConf, new UserConnectedToGlobalAudio(voiceConf, voiceConf, userid, name)))
}
def userDisconnectedFromGlobalAudio(voiceConf: String, userid: String, name: String) {
// we are required to pass the meeting_id as first parameter (just to satisfy trait)
// but it's not used anywhere. That's why we pass voiceConf twice instead
bbbActor ! new UserDisconnectedFromGlobalAudio(voiceConf, voiceConf, userid, name)
eventBus.publish(BigBlueButtonEvent(voiceConf, new UserDisconnectedFromGlobalAudio(voiceConf, voiceConf, userid, name)))
}
/**
@ -219,19 +269,19 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
*/
def clear(meetingID: String) {
bbbActor ! new ClearPresentation(meetingID)
eventBus.publish(BigBlueButtonEvent(meetingID, new ClearPresentation(meetingID)))
}
def sendConversionUpdate(messageKey: String, meetingId: String, code: String, presentationId: String, presName: String) {
bbbActor ! new PresentationConversionUpdate(meetingId, messageKey, code, presentationId, presName)
eventBus.publish(BigBlueButtonEvent(meetingId, new PresentationConversionUpdate(meetingId, messageKey, code, presentationId, presName)))
}
def sendPageCountError(messageKey: String, meetingId: String, code: String, presentationId: String, numberOfPages: Int, maxNumberPages: Int, presName: String) {
bbbActor ! new PresentationPageCountError(meetingId, messageKey, code, presentationId, numberOfPages, maxNumberPages, presName)
eventBus.publish(BigBlueButtonEvent(meetingId, new PresentationPageCountError(meetingId, messageKey, code, presentationId, numberOfPages, maxNumberPages, presName)))
}
def sendSlideGenerated(messageKey: String, meetingId: String, code: String, presentationId: String, numberOfPages: Int, pagesCompleted: Int, presName: String) {
bbbActor ! new PresentationSlideGenerated(meetingId, messageKey, code, presentationId, numberOfPages, pagesCompleted, presName)
eventBus.publish(BigBlueButtonEvent(meetingId, new PresentationSlideGenerated(meetingId, messageKey, code, presentationId, numberOfPages, pagesCompleted, presName)))
}
def generatePresentationPages(presId: String, numPages: Int, presBaseUrl: String): scala.collection.immutable.HashMap[String, Page] = {
@ -260,37 +310,37 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
val pages = generatePresentationPages(presentationId, numPages, presBaseUrl)
val presentation = new Presentation(id = presentationId, name = presName, pages = pages)
bbbActor ! new PresentationConversionCompleted(meetingId, messageKey, code, presentation)
eventBus.publish(BigBlueButtonEvent(meetingId, new PresentationConversionCompleted(meetingId, messageKey, code, presentation)))
}
def removePresentation(meetingID: String, presentationID: String) {
bbbActor ! new RemovePresentation(meetingID, presentationID)
eventBus.publish(BigBlueButtonEvent(meetingID, new RemovePresentation(meetingID, presentationID)))
}
def getPresentationInfo(meetingID: String, requesterID: String, replyTo: String) {
bbbActor ! new GetPresentationInfo(meetingID, requesterID, replyTo)
eventBus.publish(BigBlueButtonEvent(meetingID, new GetPresentationInfo(meetingID, requesterID, replyTo)))
}
def sendCursorUpdate(meetingID: String, xPercent: Double, yPercent: Double) {
bbbActor ! new SendCursorUpdate(meetingID, xPercent, yPercent)
eventBus.publish(BigBlueButtonEvent(meetingID, new SendCursorUpdate(meetingID, xPercent, yPercent)))
}
def resizeAndMoveSlide(meetingID: String, xOffset: Double, yOffset: Double, widthRatio: Double, heightRatio: Double) {
bbbActor ! new ResizeAndMoveSlide(meetingID, xOffset, yOffset, widthRatio, heightRatio)
eventBus.publish(BigBlueButtonEvent(meetingID, new ResizeAndMoveSlide(meetingID, xOffset, yOffset, widthRatio, heightRatio)))
}
def gotoSlide(meetingID: String, pageId: String) {
// println("**** Forwarding GotoSlide for meeting[" + meetingID + "] ****")
bbbActor ! new GotoSlide(meetingID, pageId)
eventBus.publish(BigBlueButtonEvent(meetingID, new GotoSlide(meetingID, pageId)))
}
def sharePresentation(meetingID: String, presentationID: String, share: Boolean) {
bbbActor ! new SharePresentation(meetingID, presentationID, share)
eventBus.publish(BigBlueButtonEvent(meetingID, new SharePresentation(meetingID, presentationID, share)))
}
def getSlideInfo(meetingID: String, requesterID: String, replyTo: String) {
bbbActor ! new GetSlideInfo(meetingID, requesterID, replyTo)
eventBus.publish(BigBlueButtonEvent(meetingID, new GetSlideInfo(meetingID, requesterID, replyTo)))
}
/**
@ -300,18 +350,18 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
*/
def getCurrentLayout(meetingID: String, requesterID: String) {
bbbActor ! new GetCurrentLayoutRequest(meetingID, requesterID)
eventBus.publish(BigBlueButtonEvent(meetingID, new GetCurrentLayoutRequest(meetingID, requesterID)))
}
def broadcastLayout(meetingID: String, requesterID: String, layout: String) {
bbbActor ! new BroadcastLayoutRequest(meetingID, requesterID, layout)
eventBus.publish(BigBlueButtonEvent(meetingID, new BroadcastLayoutRequest(meetingID, requesterID, layout)))
}
def lockLayout(meetingId: String, setById: String, lock: Boolean, viewersOnly: Boolean, layout: String) {
if (layout != null) {
bbbActor ! new LockLayoutRequest(meetingId, setById, lock, viewersOnly, Some(layout))
eventBus.publish(BigBlueButtonEvent(meetingId, new LockLayoutRequest(meetingId, setById, lock, viewersOnly, Some(layout))))
} else {
bbbActor ! new LockLayoutRequest(meetingId, setById, lock, viewersOnly, None)
eventBus.publish(BigBlueButtonEvent(meetingId, new LockLayoutRequest(meetingId, setById, lock, viewersOnly, None)))
}
}
@ -323,16 +373,16 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
*/
def getChatHistory(meetingID: String, requesterID: String, replyTo: String) {
bbbActor ! new GetChatHistoryRequest(meetingID, requesterID, replyTo)
eventBus.publish(BigBlueButtonEvent(meetingID, new GetChatHistoryRequest(meetingID, requesterID, replyTo)))
}
def sendPublicMessage(meetingID: String, requesterID: String, message: java.util.Map[String, String]) {
// Convert java Map to Scala Map, then convert Mutable map to immutable map
bbbActor ! new SendPublicMessageRequest(meetingID, requesterID, mapAsScalaMap(message).toMap)
eventBus.publish(BigBlueButtonEvent(meetingID, new SendPublicMessageRequest(meetingID, requesterID, mapAsScalaMap(message).toMap)))
}
def sendPrivateMessage(meetingID: String, requesterID: String, message: java.util.Map[String, String]) {
bbbActor ! new SendPrivateMessageRequest(meetingID, requesterID, mapAsScalaMap(message).toMap)
eventBus.publish(BigBlueButtonEvent(meetingID, new SendPrivateMessageRequest(meetingID, requesterID, mapAsScalaMap(message).toMap)))
}
/**
@ -361,30 +411,30 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
buildAnnotation(ann) match {
case Some(shape) => {
bbbActor ! new SendWhiteboardAnnotationRequest(meetingID, requesterID, shape)
eventBus.publish(BigBlueButtonEvent(meetingID, new SendWhiteboardAnnotationRequest(meetingID, requesterID, shape)))
}
case None => // do nothing
}
}
def requestWhiteboardAnnotationHistory(meetingID: String, requesterID: String, whiteboardId: String, replyTo: String) {
bbbActor ! new GetWhiteboardShapesRequest(meetingID, requesterID, whiteboardId, replyTo)
eventBus.publish(BigBlueButtonEvent(meetingID, new GetWhiteboardShapesRequest(meetingID, requesterID, whiteboardId, replyTo)))
}
def clearWhiteboard(meetingID: String, requesterID: String, whiteboardId: String) {
bbbActor ! new ClearWhiteboardRequest(meetingID, requesterID, whiteboardId)
eventBus.publish(BigBlueButtonEvent(meetingID, new ClearWhiteboardRequest(meetingID, requesterID, whiteboardId)))
}
def undoWhiteboard(meetingID: String, requesterID: String, whiteboardId: String) {
bbbActor ! new UndoWhiteboardRequest(meetingID, requesterID, whiteboardId)
eventBus.publish(BigBlueButtonEvent(meetingID, new UndoWhiteboardRequest(meetingID, requesterID, whiteboardId)))
}
def enableWhiteboard(meetingID: String, requesterID: String, enable: java.lang.Boolean) {
bbbActor ! new EnableWhiteboardRequest(meetingID, requesterID, enable)
eventBus.publish(BigBlueButtonEvent(meetingID, new EnableWhiteboardRequest(meetingID, requesterID, enable)))
}
def isWhiteboardEnabled(meetingID: String, requesterID: String, replyTo: String) {
bbbActor ! new IsWhiteboardEnabledRequest(meetingID, requesterID, replyTo)
eventBus.publish(BigBlueButtonEvent(meetingID, new IsWhiteboardEnabledRequest(meetingID, requesterID, replyTo)))
}
/**
@ -394,55 +444,53 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
*/
def muteAllExceptPresenter(meetingID: String, requesterID: String, mute: java.lang.Boolean) {
bbbActor ! new MuteAllExceptPresenterRequest(meetingID, requesterID, mute)
eventBus.publish(BigBlueButtonEvent(meetingID, new MuteAllExceptPresenterRequest(meetingID, requesterID, mute)))
}
def muteAllUsers(meetingID: String, requesterID: String, mute: java.lang.Boolean) {
bbbActor ! new MuteMeetingRequest(meetingID, requesterID, mute)
eventBus.publish(BigBlueButtonEvent(meetingID, new MuteMeetingRequest(meetingID, requesterID, mute)))
}
def isMeetingMuted(meetingID: String, requesterID: String) {
bbbActor ! new IsMeetingMutedRequest(meetingID, requesterID)
eventBus.publish(BigBlueButtonEvent(meetingID, new IsMeetingMutedRequest(meetingID, requesterID)))
}
def muteUser(meetingID: String, requesterID: String, userID: String, mute: java.lang.Boolean) {
bbbActor ! new MuteUserRequest(meetingID, requesterID, userID, mute)
eventBus.publish(BigBlueButtonEvent(meetingID, new MuteUserRequest(meetingID, requesterID, userID, mute)))
}
def lockMuteUser(meetingID: String, requesterID: String, userID: String, lock: java.lang.Boolean) {
bbbActor ! new LockUserRequest(meetingID, requesterID, userID, lock)
eventBus.publish(BigBlueButtonEvent(meetingID, new LockUserRequest(meetingID, requesterID, userID, lock)))
}
def ejectUserFromVoice(meetingId: String, userId: String, ejectedBy: String) {
bbbActor ! new EjectUserFromVoiceRequest(meetingId, userId, ejectedBy)
eventBus.publish(BigBlueButtonEvent(meetingId, new EjectUserFromVoiceRequest(meetingId, userId, ejectedBy)))
}
def voiceUserJoined(voiceConfId: String, voiceUserId: String, userId: String, callerIdName: String,
callerIdNum: String, muted: java.lang.Boolean, avatarURL: String, talking: java.lang.Boolean) {
bbbActor ! new UserJoinedVoiceConfMessage(voiceConfId, voiceUserId, userId, userId, callerIdName,
callerIdNum, muted, talking, avatarURL, false /*hardcode listenOnly to false as the message for listenOnly is ConnectedToGlobalAudio*/ )
eventBus.publish(BigBlueButtonEvent(voiceConfId, new UserJoinedVoiceConfMessage(voiceConfId, voiceUserId, userId, userId, callerIdName,
callerIdNum, muted, talking, avatarURL, false /*hardcode listenOnly to false as the message for listenOnly is ConnectedToGlobalAudio*/ )))
}
def voiceUserLeft(voiceConfId: String, voiceUserId: String) {
bbbActor ! new UserLeftVoiceConfMessage(voiceConfId, voiceUserId)
eventBus.publish(BigBlueButtonEvent(voiceConfId, new UserLeftVoiceConfMessage(voiceConfId, voiceUserId)))
}
def voiceUserLocked(voiceConfId: String, voiceUserId: String, locked: java.lang.Boolean) {
bbbActor ! new UserLockedInVoiceConfMessage(voiceConfId, voiceUserId, locked)
eventBus.publish(BigBlueButtonEvent(voiceConfId, new UserLockedInVoiceConfMessage(voiceConfId, voiceUserId, locked)))
}
def voiceUserMuted(voiceConfId: String, voiceUserId: String, muted: java.lang.Boolean) {
bbbActor ! new UserMutedInVoiceConfMessage(voiceConfId, voiceUserId, muted)
eventBus.publish(BigBlueButtonEvent(voiceConfId, new UserMutedInVoiceConfMessage(voiceConfId, voiceUserId, muted)))
}
def voiceUserTalking(voiceConfId: String, voiceUserId: String, talking: java.lang.Boolean) {
bbbActor ! new UserTalkingInVoiceConfMessage(voiceConfId, voiceUserId, talking)
eventBus.publish(BigBlueButtonEvent(voiceConfId, new UserTalkingInVoiceConfMessage(voiceConfId, voiceUserId, talking)))
}
def voiceRecording(voiceConfId: String, recordingFile: String, timestamp: String, recording: java.lang.Boolean) {
bbbActor ! new VoiceConfRecordingStartedMessage(voiceConfId, recordingFile, recording, timestamp)
eventBus.publish(BigBlueButtonEvent(voiceConfId, new VoiceConfRecordingStartedMessage(voiceConfId, recordingFile, recording, timestamp)))
}
/**
@ -450,44 +498,62 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
* Message Interface for DeskShare
* *****************************************************************
*/
def deskShareStarted(conferenceName: String, callerId: String, callerIdName: String) {
bbbActor ! new DeskShareStartedRequest(conferenceName, callerId, callerIdName)
def deskShareStarted(meetingId: String, callerId: String, callerIdName: String) {
eventBus.publish(BigBlueButtonEvent(meetingId, new DeskShareStartedRequest(meetingId, callerId, callerIdName)))
}
def deskShareStopped(conferenceName: String, callerId: String, callerIdName: String) {
bbbActor ! new DeskShareStoppedRequest(conferenceName, callerId, callerIdName)
def deskShareStopped(meetingId: String, callerId: String, callerIdName: String) {
eventBus.publish(BigBlueButtonEvent(meetingId, new DeskShareStoppedRequest(meetingId, callerId, callerIdName)))
}
def deskShareRTMPBroadcastStarted(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) {
bbbActor ! new DeskShareRTMPBroadcastStartedRequest(conferenceName, streamname, videoWidth, videoHeight, timestamp)
def deskShareRTMPBroadcastStarted(meetingId: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) {
eventBus.publish(BigBlueButtonEvent(meetingId, new DeskShareRTMPBroadcastStartedRequest(meetingId, streamname, videoWidth, videoHeight, timestamp)))
}
def deskShareRTMPBroadcastStopped(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) {
bbbActor ! new DeskShareRTMPBroadcastStoppedRequest(conferenceName, streamname, videoWidth, videoHeight, timestamp)
def deskShareRTMPBroadcastStopped(meetingId: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) {
eventBus.publish(BigBlueButtonEvent(meetingId, new DeskShareRTMPBroadcastStoppedRequest(meetingId, streamname, videoWidth, videoHeight, timestamp)))
}
def deskShareGetInfoRequest(meetingId: String, requesterId: String, replyTo: String): Unit = {
bbbActor ! new DeskShareGetDeskShareInfoRequest(meetingId, requesterId, replyTo)
eventBus.publish(BigBlueButtonEvent(meetingId, new DeskShareGetDeskShareInfoRequest(meetingId, requesterId, replyTo)))
}
// Polling
def votePoll(meetingId: String, userId: String, pollId: String, questionId: Integer, answerId: Integer) {
bbbActor ! new RespondToPollRequest(meetingId, userId, pollId, questionId, answerId)
eventBus.publish(BigBlueButtonEvent(meetingId, new RespondToPollRequest(meetingId, userId, pollId, questionId, answerId)))
}
def startPoll(meetingId: String, requesterId: String, pollId: String, pollType: String) {
bbbActor ! new StartPollRequest(meetingId, requesterId, pollType)
eventBus.publish(BigBlueButtonEvent(meetingId, new StartPollRequest(meetingId, requesterId, pollType)))
}
def stopPoll(meetingId: String, userId: String, pollId: String) {
bbbActor ! new StopPollRequest(meetingId, userId)
eventBus.publish(BigBlueButtonEvent(meetingId, new StopPollRequest(meetingId, userId)))
}
def showPollResult(meetingId: String, requesterId: String, pollId: String, show: java.lang.Boolean) {
if (show) {
bbbActor ! new ShowPollResultRequest(meetingId, requesterId, pollId)
eventBus.publish(BigBlueButtonEvent(meetingId, new ShowPollResultRequest(meetingId, requesterId, pollId)))
} else {
bbbActor ! new HidePollResultRequest(meetingId, requesterId, pollId)
eventBus.publish(BigBlueButtonEvent(meetingId, new HidePollResultRequest(meetingId, requesterId, pollId)))
}
}
/**
* *******************************************************************
* Message Interface for Caption
* *****************************************************************
*/
def sendCaptionHistory(meetingID: String, requesterID: String) {
eventBus.publish(BigBlueButtonEvent(meetingID, new SendCaptionHistoryRequest(meetingID, requesterID)))
}
def updateCaptionOwner(meetingID: String, locale: String, ownerID: String) {
eventBus.publish(BigBlueButtonEvent(meetingID, new UpdateCaptionOwnerRequest(meetingID, locale, ownerID)))
}
def editCaptionHistory(meetingID: String, userID: String, startIndex: Integer, endIndex: Integer, locale: String, text: String) {
eventBus.publish(BigBlueButtonEvent(meetingID, new EditCaptionHistoryRequest(meetingID, userID, startIndex, endIndex, locale, text)))
}
}

View File

@ -0,0 +1,9 @@
package org.bigbluebutton.core {
object MessageType extends scala.Enumeration {
type MessageType = Value
val SYSTEM = Value("system")
val BROADCAST = Value("broadcast")
val DIRECT = Value("direct")
}
}

View File

@ -0,0 +1,78 @@
package org.bigbluebutton.core
import scala.util.{ Try, Success, Failure }
import spray.json.{ JsObject, JsonParser, DeserializationException }
import org.parboiled.errors.ParsingException
import org.bigbluebutton.core.api._
import org.bigbluebutton.messages._
object JsonMessageDecoder {
import org.bigbluebutton.core.UserMessagesProtocol._
import spray.json._
def header(msg: JsObject): InMessageHeader = {
msg.fields.get("header") match {
case Some(header) =>
header.convertTo[InMessageHeader]
case None =>
throw MessageProcessException("Cannot get payload information: [" + msg + "]")
}
}
def payload(msg: JsObject): JsObject = {
msg.fields.get("payload") match {
case Some(payload) =>
payload.asJsObject
case None =>
throw MessageProcessException("Cannot get payload information: [" + msg + "]")
}
}
def toJsObject(msg: String): JsObject = {
try {
JsonParser(msg).asJsObject
} catch {
case e: ParsingException => {
throw MessageProcessException("Cannot parse JSON message: [" + msg + "]")
}
}
}
def unmarshall(jsonMsg: String): Try[InMessage] = {
for {
jsonObj <- Try(toJsObject(jsonMsg))
header <- Try(header(jsonObj))
payload <- Try(payload(jsonObj))
msg = InHeaderAndJsonPayload(header, payload)
inmsg <- Try(convertMessage(msg))
} yield inmsg
}
def decode(json: String): Option[InMessage] = {
unmarshall(json) match {
case Success(validMsg) => Some(validMsg)
case Failure(ex) => None
}
}
def convertMessage(msg: InHeaderAndJsonPayload): InMessage = {
msg.header.name match {
case GetBreakoutRoomsList.NAME => {
msg.payload.convertTo[BreakoutRoomsListMessage]
}
case CreateBreakoutRoomsRequest.NAME => {
msg.payload.convertTo[CreateBreakoutRooms]
}
case RequestBreakoutJoinURL.NAME => {
msg.payload.convertTo[RequestBreakoutJoinURLInMessage]
}
case ListenInOnBreakout.NAME => {
msg.payload.convertTo[TransferUserToMeetingRequest]
}
case EndAllBreakoutRoomsRequest.NAME => {
msg.payload.convertTo[EndAllBreakoutRooms]
}
case _ => throw MessageProcessException("Cannot parse JSON message: [" + msg + "]")
}
}
}

View File

@ -0,0 +1,114 @@
package org.bigbluebutton.core
import akka.actor.Actor
import akka.actor.ActorRef
import akka.actor.ActorLogging
import akka.actor.Props
import org.bigbluebutton.core.api._
import org.bigbluebutton.common.messages.MessagingConstants
import org.bigbluebutton.core.pubsub.senders.ChatMessageToJsonConverter
import org.bigbluebutton.common.messages.StartRecordingVoiceConfRequestMessage
import org.bigbluebutton.common.messages.StopRecordingVoiceConfRequestMessage
import org.bigbluebutton.core.pubsub.senders.MeetingMessageToJsonConverter
import org.bigbluebutton.core.pubsub.senders.PesentationMessageToJsonConverter
import org.bigbluebutton.common.messages.GetPresentationInfoReplyMessage
import org.bigbluebutton.common.messages.PresentationRemovedMessage
import org.bigbluebutton.core.apps.Page
import collection.JavaConverters._
import scala.collection.JavaConversions._
import org.bigbluebutton.core.apps.SimplePollResultOutVO
import org.bigbluebutton.core.apps.SimplePollOutVO
import org.bigbluebutton.core.pubsub.senders.UsersMessageToJsonConverter
import org.bigbluebutton.common.messages._
import org.bigbluebutton.core.pubsub.senders.WhiteboardMessageToJsonConverter
import org.bigbluebutton.common.converters.ToJsonEncoder
import org.bigbluebutton.common.messages.payload._
import org.bigbluebutton.common.messages._
import org.bigbluebutton.messages.payload._
import org.bigbluebutton.messages._
object JsonMessageSenderActor {
def props(msgSender: MessageSender): Props =
Props(classOf[JsonMessageSenderActor], msgSender)
}
class JsonMessageSenderActor(val service: MessageSender)
extends Actor with ActorLogging {
def receive = {
// Breakout
case msg: CreateBreakoutRoom => handleCreateBreakoutRoom(msg)
case msg: EndBreakoutRoom => handleEndBreakoutRoom(msg)
case msg: BreakoutRoomsListOutMessage => handleBreakoutRoomsList(msg)
case msg: BreakoutRoomJoinURLOutMessage => handleBreakoutRoomJoinURL(msg)
case msg: BreakoutRoomStartedOutMessage => handleBreakoutRoomStarted(msg)
case msg: BreakoutRoomEndedOutMessage => handleBreakoutRoomEnded(msg)
case msg: UpdateBreakoutUsersOutMessage => handleUpdateBreakoutUsers(msg)
case msg: MeetingTimeRemainingUpdate => handleMeetingTimeRemainingUpdate(msg)
case _ => // do nothing
}
// Breakout
private def handleBreakoutRoomStarted(msg: BreakoutRoomStartedOutMessage) {
val payload = new BreakoutRoomPayload(msg.meetingId, msg.breakout.breakoutId, msg.breakout.name)
val request = new BreakoutRoomStarted(payload)
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson)
}
private def handleBreakoutRoomEnded(msg: BreakoutRoomEndedOutMessage) {
val payload = new BreakoutRoomPayload(msg.meetingId, msg.breakoutId, "")
val request = new BreakoutRoomClosed(payload)
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson)
}
private def handleUpdateBreakoutUsers(msg: UpdateBreakoutUsersOutMessage) {
val users = new java.util.ArrayList[BreakoutUserPayload]()
msg.users.foreach(x => users.add(new BreakoutUserPayload(x.id, x.name)))
val payload = new UpdateBreakoutUsersPayload(msg.meetingId, msg.breakoutId, users)
val request = new UpdateBreakoutUsers(payload)
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
}
private def handleMeetingTimeRemainingUpdate(msg: MeetingTimeRemainingUpdate) {
val payload = new MeetingTimeRemainingPayload(msg.meetingId, msg.timeRemaining)
val request = new TimeRemainingUpdate(payload)
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
}
private def handleMeetingTimeRemainingUpdate(msg: BreakoutRoomsTimeRemainingUpdateOutMessage) {
val payload = new BreakoutRoomsTimeRemainingPayload(msg.meetingId, msg.timeRemaining)
val request = new BreakoutRoomsTimeRemainingUpdate(payload)
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
}
private def handleBreakoutRoomsList(msg: BreakoutRoomsListOutMessage) {
val rooms = new java.util.ArrayList[BreakoutRoomPayload]()
msg.rooms.foreach(r => rooms.add(new BreakoutRoomPayload(msg.meetingId, r.breakoutId, r.name)))
val payload = new BreakoutRoomsListPayload(msg.meetingId, rooms)
val request = new BreakoutRoomsList(payload)
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
}
private def handleCreateBreakoutRoom(msg: CreateBreakoutRoom) {
val payload = new CreateBreakoutRoomRequestPayload(msg.room.breakoutId, msg.room.parentId, msg.room.name,
msg.room.voiceConfId, msg.room.viewerPassword, msg.room.moderatorPassword,
msg.room.durationInMinutes, msg.room.defaultPresentationURL)
val request = new CreateBreakoutRoomRequest(payload)
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
}
private def handleEndBreakoutRoom(msg: EndBreakoutRoom) {
val payload = new EndBreakoutRoomRequestPayload(msg.breakoutId)
val request = new EndBreakoutRoomRequest(payload)
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
}
def handleBreakoutRoomJoinURL(msg: BreakoutRoomJoinURLOutMessage) {
val payload = new BreakoutRoomJoinURLPayload(msg.meetingId,
msg.breakoutId, msg.userId, msg.joinURL)
val request = new BreakoutRoomJoinURL(payload)
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson)
}
}

View File

@ -0,0 +1,160 @@
package org.bigbluebutton.core
import java.util.concurrent.TimeUnit
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.apps._
import org.bigbluebutton.core.bus.IncomingEventBus
import akka.actor.ActorContext
import akka.event.Logging
import org.bigbluebutton.core.apps.CaptionApp
import org.bigbluebutton.core.apps.CaptionModel
class LiveMeeting(val mProps: MeetingProperties,
val eventBus: IncomingEventBus,
val outGW: OutMessageGateway,
val chatModel: ChatModel,
val layoutModel: LayoutModel,
val meetingModel: MeetingModel,
val usersModel: UsersModel,
val pollModel: PollModel,
val wbModel: WhiteboardModel,
val presModel: PresentationModel,
val breakoutModel: BreakoutRoomModel,
val captionModel: CaptionModel)(implicit val context: ActorContext)
extends UsersApp with PresentationApp
with LayoutApp with ChatApp with WhiteboardApp with PollApp
with BreakoutRoomApp with CaptionApp {
val log = Logging(context.system, getClass)
def hasMeetingEnded(): Boolean = {
meetingModel.hasMeetingEnded()
}
def webUserJoined() {
if (usersModel.numWebUsers > 0) {
meetingModel.resetLastWebUserLeftOn()
}
}
def startRecordingIfAutoStart() {
if (mProps.recorded && !meetingModel.isRecording() && mProps.autoStartRecording && usersModel.numWebUsers == 1) {
log.info("Auto start recording. meetingId={}", mProps.meetingID)
meetingModel.recordingStarted()
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", meetingModel.isRecording()))
}
}
def stopAutoStartedRecording() {
if (mProps.recorded && meetingModel.isRecording() && mProps.autoStartRecording && usersModel.numWebUsers == 0) {
log.info("Last web user left. Auto stopping recording. meetingId={}", mProps.meetingID)
meetingModel.recordingStopped()
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", meetingModel.isRecording()))
}
}
def startCheckingIfWeNeedToEndVoiceConf() {
if (usersModel.numWebUsers == 0) {
meetingModel.lastWebUserLeft()
log.debug("MonitorNumberOfWebUsers started for meeting [" + mProps.meetingID + "]")
}
}
def sendTimeRemainingNotice() {
val now = timeNowInSeconds
if (mProps.duration > 0 && (((meetingModel.startedOn + mProps.duration) - now) < 15)) {
// log.warning("MEETING WILL END IN 15 MINUTES!!!!")
}
}
def handleMonitorNumberOfWebUsers(msg: MonitorNumberOfUsers) {
if (usersModel.numWebUsers == 0 && meetingModel.lastWebUserLeftOn > 0) {
if (timeNowInMinutes - meetingModel.lastWebUserLeftOn > 2) {
log.info("Empty meeting. Ejecting all users from voice. meetingId={}", mProps.meetingID)
outGW.send(new EjectAllVoiceUsers(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
}
}
}
def handleSendTimeRemainingUpdate(msg: SendTimeRemainingUpdate) {
if (mProps.duration > 0) {
val endMeetingTime = meetingModel.startedOn + (mProps.duration * 60)
val timeRemaining = endMeetingTime - timeNowInSeconds
outGW.send(new MeetingTimeRemainingUpdate(mProps.meetingID, mProps.recorded, timeRemaining.toInt))
}
if (!mProps.isBreakout && breakoutModel.getRooms().length > 0) {
val room = breakoutModel.getRooms()(0);
val endMeetingTime = meetingModel.breakoutRoomsStartedOn + (meetingModel.breakoutRoomsdurationInMinutes * 60)
val timeRemaining = endMeetingTime - timeNowInSeconds
outGW.send(new BreakoutRoomsTimeRemainingUpdateOutMessage(mProps.meetingID, mProps.recorded, timeRemaining.toInt))
} else if (meetingModel.breakoutRoomsStartedOn != 0) {
meetingModel.breakoutRoomsdurationInMinutes = 0;
meetingModel.breakoutRoomsStartedOn = 0;
}
}
def handleExtendMeetingDuration(msg: ExtendMeetingDuration) {
}
def timeNowInMinutes(): Long = {
TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
}
def timeNowInSeconds(): Long = {
TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
}
def sendMeetingHasEnded(userId: String) {
outGW.send(new MeetingHasEnded(mProps.meetingID, userId))
outGW.send(new DisconnectUser(mProps.meetingID, userId))
}
def handleEndMeeting(msg: EndMeeting) {
meetingModel.meetingHasEnded
outGW.send(new MeetingEnded(msg.meetingId, mProps.recorded, mProps.voiceBridge))
outGW.send(new DisconnectAllUsers(msg.meetingId))
}
def handleVoiceConfRecordingStartedMessage(msg: VoiceConfRecordingStartedMessage) {
if (msg.recording) {
meetingModel.setVoiceRecordingFilename(msg.recordStream)
outGW.send(new VoiceRecordingStarted(mProps.meetingID, mProps.recorded, msg.recordStream, msg.timestamp, mProps.voiceBridge))
} else {
meetingModel.setVoiceRecordingFilename("")
outGW.send(new VoiceRecordingStopped(mProps.meetingID, mProps.recorded, msg.recordStream, msg.timestamp, mProps.voiceBridge))
}
}
def handleSetRecordingStatus(msg: SetRecordingStatus) {
log.info("Change recording status. meetingId=" + mProps.meetingID + " recording=" + msg.recording)
if (mProps.allowStartStopRecording && meetingModel.isRecording() != msg.recording) {
if (msg.recording) {
meetingModel.recordingStarted()
} else {
meetingModel.recordingStopped()
}
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, msg.userId, msg.recording))
}
}
def handleGetRecordingStatus(msg: GetRecordingStatus) {
outGW.send(new GetRecordingStatusReply(mProps.meetingID, mProps.recorded, msg.userId, meetingModel.isRecording().booleanValue()))
}
def lockLayout(lock: Boolean) {
meetingModel.lockLayout(lock)
}
def newPermissions(np: Permissions) {
meetingModel.setPermissions(np)
}
def permissionsEqual(other: Permissions): Boolean = {
meetingModel.permissionsEqual(other)
}
}

View File

@ -4,23 +4,72 @@ import akka.actor.Actor
import akka.actor.ActorRef
import akka.actor.ActorLogging
import akka.actor.Props
import org.bigbluebutton.core.bus._
import org.bigbluebutton.core.api._
import java.util.concurrent.TimeUnit
import org.bigbluebutton.core.util._
import scala.concurrent.duration._
import org.bigbluebutton.core.apps.{ PollApp, UsersApp, PresentationApp, LayoutApp, ChatApp, WhiteboardApp }
import org.bigbluebutton.core.apps.{ ChatModel, LayoutModel, UsersModel, PollModel, WhiteboardModel }
import org.bigbluebutton.core.apps.{ PollApp, UsersApp, PresentationApp, LayoutApp, ChatApp, WhiteboardApp, CaptionApp }
import org.bigbluebutton.core.apps.{ ChatModel, LayoutModel, UsersModel, PollModel, WhiteboardModel, CaptionModel }
import org.bigbluebutton.core.apps.PresentationModel
import org.bigbluebutton.core.apps.BreakoutRoomApp
import org.bigbluebutton.core.apps.BreakoutRoomModel
object MeetingActor {
def props(mProps: MeetingProperties, outGW: OutMessageGateway): Props =
Props(classOf[MeetingActor], mProps: MeetingProperties, outGW)
object MeetingActorInternal {
def props(mProps: MeetingProperties,
eventBus: IncomingEventBus,
outGW: OutMessageGateway): Props =
Props(classOf[MeetingActorInternal], mProps, eventBus, outGW)
}
class MeetingActor(val mProps: MeetingProperties, val outGW: OutMessageGateway)
extends Actor with UsersApp with PresentationApp
with LayoutApp with ChatApp with WhiteboardApp with PollApp
with ActorLogging {
// This actor is an internal audit actor for each meeting actor that
// periodically sends messages to the meeting actor
class MeetingActorInternal(val mProps: MeetingProperties,
val eventBus: IncomingEventBus, val outGW: OutMessageGateway)
extends Actor with ActorLogging {
import context.dispatcher
context.system.scheduler.schedule(2 seconds, 30 seconds, self, "MonitorNumberOfWebUsers")
// Query to get voice conference users
outGW.send(new GetUsersInVoiceConference(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
if (mProps.isBreakout) {
// This is a breakout room. Inform our parent meeting that we have been successfully created.
eventBus.publish(BigBlueButtonEvent(
mProps.externalMeetingID,
BreakoutRoomCreated(mProps.externalMeetingID, mProps.meetingID)))
}
def receive = {
case "MonitorNumberOfWebUsers" => handleMonitorNumberOfWebUsers()
}
def handleMonitorNumberOfWebUsers() {
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, MonitorNumberOfUsers(mProps.meetingID)))
// Trigger updating users of time remaining on meeting.
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, SendTimeRemainingUpdate(mProps.meetingID)))
if (mProps.isBreakout) {
// This is a breakout room. Update the main meeting with list of users in this breakout room.
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, SendBreakoutUsersUpdate(mProps.meetingID)))
}
}
}
object MeetingActor {
def props(mProps: MeetingProperties,
eventBus: IncomingEventBus,
outGW: OutMessageGateway): Props =
Props(classOf[MeetingActor], mProps, eventBus, outGW)
}
class MeetingActor(val mProps: MeetingProperties,
val eventBus: IncomingEventBus,
val outGW: OutMessageGateway)
extends Actor with ActorLogging {
val chatModel = new ChatModel()
val layoutModel = new LayoutModel()
@ -29,280 +78,109 @@ class MeetingActor(val mProps: MeetingProperties, val outGW: OutMessageGateway)
val pollModel = new PollModel()
val wbModel = new WhiteboardModel()
val presModel = new PresentationModel()
val breakoutModel = new BreakoutRoomModel()
val captionModel = new CaptionModel()
import context.dispatcher
context.system.scheduler.schedule(2 seconds, 30 seconds, self, "MonitorNumberOfWebUsers")
// We extract the meeting handlers into this class so it is
// easy to test.
val liveMeeting = new LiveMeeting(mProps, eventBus, outGW,
chatModel, layoutModel, meetingModel, usersModel, pollModel,
wbModel, presModel, breakoutModel, captionModel)
outGW.send(new GetUsersInVoiceConference(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
/**
* Put the internal message injector into another actor so this
* actor is easy to test.
*/
var actorMonitor = context.actorOf(MeetingActorInternal.props(mProps, eventBus, outGW))
def receive = {
case "StartTimer" =>
handleStartTimer
case "Hello" =>
handleHello
case "MonitorNumberOfWebUsers" =>
handleMonitorNumberOfWebUsers()
case msg: ValidateAuthToken =>
handleValidateAuthToken(msg)
case msg: RegisterUser =>
handleRegisterUser(msg)
case msg: UserJoinedVoiceConfMessage =>
handleUserJoinedVoiceConfMessage(msg)
case msg: UserLeftVoiceConfMessage =>
handleUserLeftVoiceConfMessage(msg)
case msg: UserMutedInVoiceConfMessage =>
handleUserMutedInVoiceConfMessage(msg)
case msg: UserTalkingInVoiceConfMessage =>
handleUserTalkingInVoiceConfMessage(msg)
case msg: VoiceConfRecordingStartedMessage =>
handleVoiceConfRecordingStartedMessage(msg)
case msg: DeskShareRTMPBroadcastStartedRequest =>
handleDeskShareRTMPBroadcastStartedRequest(msg)
case msg: DeskShareRTMPBroadcastStoppedRequest =>
handleDeskShareRTMPBroadcastStoppedRequest(msg)
case msg: DeskShareStartedRequest =>
handleDeskShareStartedRequest(msg)
case msg: DeskShareStoppedRequest =>
handleDeskShareStoppedRequest(msg)
case msg: DeskShareGetDeskShareInfoRequest =>
handleDeskShareGetDeskShareInfoRequest(msg)
case msg: UserJoining =>
handleUserJoin(msg)
case msg: UserLeaving =>
handleUserLeft(msg)
case msg: AssignPresenter =>
handleAssignPresenter(msg)
case msg: GetUsers =>
handleGetUsers(msg)
case msg: ChangeUserStatus =>
handleChangeUserStatus(msg)
case msg: EjectUserFromMeeting =>
handleEjectUserFromMeeting(msg)
case msg: UserEmojiStatus =>
handleUserEmojiStatus(msg)
case msg: UserShareWebcam =>
handleUserShareWebcam(msg)
case msg: UserUnshareWebcam =>
handleUserunshareWebcam(msg)
case msg: MuteMeetingRequest =>
handleMuteMeetingRequest(msg)
case msg: MuteAllExceptPresenterRequest =>
handleMuteAllExceptPresenterRequest(msg)
case msg: IsMeetingMutedRequest =>
handleIsMeetingMutedRequest(msg)
case msg: MuteUserRequest =>
handleMuteUserRequest(msg)
case msg: EjectUserFromVoiceRequest =>
handleEjectUserRequest(msg)
case msg: SetLockSettings =>
handleSetLockSettings(msg)
case msg: GetLockSettings =>
handleGetLockSettings(msg)
case msg: LockUserRequest =>
handleLockUserRequest(msg)
case msg: InitLockSettings =>
handleInitLockSettings(msg)
case msg: InitAudioSettings =>
handleInitAudioSettings(msg)
case msg: GetChatHistoryRequest =>
handleGetChatHistoryRequest(msg)
case msg: SendPublicMessageRequest =>
handleSendPublicMessageRequest(msg)
case msg: SendPrivateMessageRequest =>
handleSendPrivateMessageRequest(msg)
case msg: UserConnectedToGlobalAudio =>
handleUserConnectedToGlobalAudio(msg)
case msg: UserDisconnectedFromGlobalAudio =>
handleUserDisconnectedFromGlobalAudio(msg)
case msg: GetCurrentLayoutRequest =>
handleGetCurrentLayoutRequest(msg)
case msg: BroadcastLayoutRequest =>
handleBroadcastLayoutRequest(msg)
case msg: InitializeMeeting =>
handleInitializeMeeting(msg)
case msg: ClearPresentation =>
handleClearPresentation(msg)
case msg: PresentationConversionUpdate =>
handlePresentationConversionUpdate(msg)
case msg: PresentationPageCountError =>
handlePresentationPageCountError(msg)
case msg: PresentationSlideGenerated =>
handlePresentationSlideGenerated(msg)
case msg: PresentationConversionCompleted =>
handlePresentationConversionCompleted(msg)
case msg: RemovePresentation =>
handleRemovePresentation(msg)
case msg: GetPresentationInfo =>
handleGetPresentationInfo(msg)
case msg: SendCursorUpdate =>
handleSendCursorUpdate(msg)
case msg: ResizeAndMoveSlide =>
handleResizeAndMoveSlide(msg)
case msg: GotoSlide =>
handleGotoSlide(msg)
case msg: SharePresentation =>
handleSharePresentation(msg)
case msg: GetSlideInfo =>
handleGetSlideInfo(msg)
case msg: PreuploadedPresentations =>
handlePreuploadedPresentations(msg)
case msg: SendWhiteboardAnnotationRequest =>
handleSendWhiteboardAnnotationRequest(msg)
case msg: GetWhiteboardShapesRequest =>
handleGetWhiteboardShapesRequest(msg)
case msg: ClearWhiteboardRequest =>
handleClearWhiteboardRequest(msg)
case msg: UndoWhiteboardRequest =>
handleUndoWhiteboardRequest(msg)
case msg: EnableWhiteboardRequest =>
handleEnableWhiteboardRequest(msg)
case msg: IsWhiteboardEnabledRequest =>
handleIsWhiteboardEnabledRequest(msg)
case msg: SetRecordingStatus =>
handleSetRecordingStatus(msg)
case msg: GetRecordingStatus =>
handleGetRecordingStatus(msg)
case msg: StartCustomPollRequest =>
handleStartCustomPollRequest(msg)
case msg: StartPollRequest =>
handleStartPollRequest(msg)
case msg: StopPollRequest =>
handleStopPollRequest(msg)
case msg: ShowPollResultRequest =>
handleShowPollResultRequest(msg)
case msg: HidePollResultRequest =>
handleHidePollResultRequest(msg)
case msg: RespondToPollRequest =>
handleRespondToPollRequest(msg)
case msg: GetPollRequest =>
handleGetPollRequest(msg)
case msg: GetCurrentPollRequest =>
handleGetCurrentPollRequest(msg)
case msg: MonitorNumberOfUsers => liveMeeting.handleMonitorNumberOfWebUsers(msg)
case msg: ValidateAuthToken => liveMeeting.handleValidateAuthToken(msg)
case msg: RegisterUser => liveMeeting.handleRegisterUser(msg)
case msg: UserJoinedVoiceConfMessage => liveMeeting.handleUserJoinedVoiceConfMessage(msg)
case msg: UserLeftVoiceConfMessage => liveMeeting.handleUserLeftVoiceConfMessage(msg)
case msg: UserMutedInVoiceConfMessage => liveMeeting.handleUserMutedInVoiceConfMessage(msg)
case msg: UserTalkingInVoiceConfMessage => liveMeeting.handleUserTalkingInVoiceConfMessage(msg)
case msg: VoiceConfRecordingStartedMessage => liveMeeting.handleVoiceConfRecordingStartedMessage(msg)
case msg: UserJoining => liveMeeting.handleUserJoin(msg)
case msg: UserLeaving => liveMeeting.handleUserLeft(msg)
case msg: AssignPresenter => liveMeeting.handleAssignPresenter(msg)
case msg: GetUsers => liveMeeting.handleGetUsers(msg)
case msg: ChangeUserStatus => liveMeeting.handleChangeUserStatus(msg)
case msg: EjectUserFromMeeting => liveMeeting.handleEjectUserFromMeeting(msg)
case msg: UserEmojiStatus => liveMeeting.handleUserEmojiStatus(msg)
case msg: UserShareWebcam => liveMeeting.handleUserShareWebcam(msg)
case msg: UserUnshareWebcam => liveMeeting.handleUserunshareWebcam(msg)
case msg: MuteMeetingRequest => liveMeeting.handleMuteMeetingRequest(msg)
case msg: MuteAllExceptPresenterRequest => liveMeeting.handleMuteAllExceptPresenterRequest(msg)
case msg: IsMeetingMutedRequest => liveMeeting.handleIsMeetingMutedRequest(msg)
case msg: MuteUserRequest => liveMeeting.handleMuteUserRequest(msg)
case msg: EjectUserFromVoiceRequest => liveMeeting.handleEjectUserRequest(msg)
case msg: TransferUserToMeetingRequest => liveMeeting.handleTransferUserToMeeting(msg)
case msg: SetLockSettings => liveMeeting.handleSetLockSettings(msg)
case msg: GetLockSettings => liveMeeting.handleGetLockSettings(msg)
case msg: LockUserRequest => liveMeeting.handleLockUserRequest(msg)
case msg: InitLockSettings => liveMeeting.handleInitLockSettings(msg)
case msg: InitAudioSettings => liveMeeting.handleInitAudioSettings(msg)
case msg: GetChatHistoryRequest => liveMeeting.handleGetChatHistoryRequest(msg)
case msg: SendPublicMessageRequest => liveMeeting.handleSendPublicMessageRequest(msg)
case msg: SendPrivateMessageRequest => liveMeeting.handleSendPrivateMessageRequest(msg)
case msg: UserConnectedToGlobalAudio => liveMeeting.handleUserConnectedToGlobalAudio(msg)
case msg: UserDisconnectedFromGlobalAudio => liveMeeting.handleUserDisconnectedFromGlobalAudio(msg)
case msg: GetCurrentLayoutRequest => liveMeeting.handleGetCurrentLayoutRequest(msg)
case msg: BroadcastLayoutRequest => liveMeeting.handleBroadcastLayoutRequest(msg)
case msg: InitializeMeeting => liveMeeting.handleInitializeMeeting(msg)
case msg: ClearPresentation => liveMeeting.handleClearPresentation(msg)
case msg: PresentationConversionUpdate => liveMeeting.handlePresentationConversionUpdate(msg)
case msg: PresentationPageCountError => liveMeeting.handlePresentationPageCountError(msg)
case msg: PresentationSlideGenerated => liveMeeting.handlePresentationSlideGenerated(msg)
case msg: PresentationConversionCompleted => liveMeeting.handlePresentationConversionCompleted(msg)
case msg: RemovePresentation => liveMeeting.handleRemovePresentation(msg)
case msg: GetPresentationInfo => liveMeeting.handleGetPresentationInfo(msg)
case msg: SendCursorUpdate => liveMeeting.handleSendCursorUpdate(msg)
case msg: ResizeAndMoveSlide => liveMeeting.handleResizeAndMoveSlide(msg)
case msg: GotoSlide => liveMeeting.handleGotoSlide(msg)
case msg: SharePresentation => liveMeeting.handleSharePresentation(msg)
case msg: GetSlideInfo => liveMeeting.handleGetSlideInfo(msg)
case msg: PreuploadedPresentations => liveMeeting.handlePreuploadedPresentations(msg)
case msg: SendWhiteboardAnnotationRequest => liveMeeting.handleSendWhiteboardAnnotationRequest(msg)
case msg: GetWhiteboardShapesRequest => liveMeeting.handleGetWhiteboardShapesRequest(msg)
case msg: ClearWhiteboardRequest => liveMeeting.handleClearWhiteboardRequest(msg)
case msg: UndoWhiteboardRequest => liveMeeting.handleUndoWhiteboardRequest(msg)
case msg: EnableWhiteboardRequest => liveMeeting.handleEnableWhiteboardRequest(msg)
case msg: IsWhiteboardEnabledRequest => liveMeeting.handleIsWhiteboardEnabledRequest(msg)
case msg: SetRecordingStatus => liveMeeting.handleSetRecordingStatus(msg)
case msg: GetRecordingStatus => liveMeeting.handleGetRecordingStatus(msg)
case msg: StartCustomPollRequest => liveMeeting.handleStartCustomPollRequest(msg)
case msg: StartPollRequest => liveMeeting.handleStartPollRequest(msg)
case msg: StopPollRequest => liveMeeting.handleStopPollRequest(msg)
case msg: ShowPollResultRequest => liveMeeting.handleShowPollResultRequest(msg)
case msg: HidePollResultRequest => liveMeeting.handleHidePollResultRequest(msg)
case msg: RespondToPollRequest => liveMeeting.handleRespondToPollRequest(msg)
case msg: GetPollRequest => liveMeeting.handleGetPollRequest(msg)
case msg: GetCurrentPollRequest => liveMeeting.handleGetCurrentPollRequest(msg)
// Breakout rooms
case msg: BreakoutRoomsListMessage => liveMeeting.handleBreakoutRoomsList(msg)
case msg: CreateBreakoutRooms => liveMeeting.handleCreateBreakoutRooms(msg)
case msg: BreakoutRoomCreated => liveMeeting.handleBreakoutRoomCreated(msg)
case msg: BreakoutRoomEnded => liveMeeting.handleBreakoutRoomEnded(msg)
case msg: RequestBreakoutJoinURLInMessage => liveMeeting.handleRequestBreakoutJoinURL(msg)
case msg: BreakoutRoomUsersUpdate => liveMeeting.handleBreakoutRoomUsersUpdate(msg)
case msg: SendBreakoutUsersUpdate => liveMeeting.handleSendBreakoutUsersUpdate(msg)
case msg: EndAllBreakoutRooms => liveMeeting.handleEndAllBreakoutRooms(msg)
case msg: ExtendMeetingDuration => liveMeeting.handleExtendMeetingDuration(msg)
case msg: SendTimeRemainingUpdate => liveMeeting.handleSendTimeRemainingUpdate(msg)
case msg: EndMeeting => liveMeeting.handleEndMeeting(msg)
// Closed Caption
case msg: SendCaptionHistoryRequest => liveMeeting.handleSendCaptionHistoryRequest(msg)
case msg: UpdateCaptionOwnerRequest => liveMeeting.handleUpdateCaptionOwnerRequest(msg)
case msg: EditCaptionHistoryRequest => liveMeeting.handleEditCaptionHistoryRequest(msg)
case msg: EndMeeting => handleEndMeeting(msg)
case StopMeetingActor => //exit
case _ => // do nothing
}
def hasMeetingEnded(): Boolean = {
meetingModel.hasMeetingEnded()
}
private def handleStartTimer() {
// println("***************timer started******************")
// val timerActor = new TimerActor(2000, self, "Hello")
// timerActor.start
}
private def handleHello() {
// println("***************hello received on [" + System.currentTimeMillis() + "]******************")
// val timerActor = new TimerActor(2000, self, "Hello")
// timerActor.start
}
def webUserJoined() {
if (usersModel.numWebUsers > 0) {
meetingModel.resetLastWebUserLeftOn()
}
}
def startRecordingIfAutoStart() {
if (mProps.recorded && !meetingModel.isRecording() && mProps.autoStartRecording && usersModel.numWebUsers == 1) {
log.info("Auto start recording. meetingId={}", mProps.meetingID)
meetingModel.recordingStarted()
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", meetingModel.isRecording()))
}
}
def stopAutoStartedRecording() {
if (mProps.recorded && meetingModel.isRecording() && mProps.autoStartRecording && usersModel.numWebUsers == 0) {
log.info("Last web user left. Auto stopping recording. meetingId={}", mProps.meetingID)
meetingModel.recordingStopped()
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", meetingModel.isRecording()))
}
}
def startCheckingIfWeNeedToEndVoiceConf() {
if (usersModel.numWebUsers == 0) {
meetingModel.lastWebUserLeft()
log.debug("MonitorNumberOfWebUsers started for meeting [" + mProps.meetingID + "]")
}
}
def handleMonitorNumberOfWebUsers() {
// println("BACK TIMER")
if (usersModel.numWebUsers == 0 && meetingModel.lastWebUserLeftOn > 0) {
if (timeNowInMinutes - meetingModel.lastWebUserLeftOn > 2) {
log.info("Empty meeting. Ejecting all users from voice. meetingId={}", mProps.meetingID)
outGW.send(new EjectAllVoiceUsers(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
}
}
val now = timeNowInMinutes
// println("(" + meetingModel.startedOn + "+" + mProps.duration + ") - " + now + " = " + ((meetingModel.startedOn + mProps.duration) - now) + " < 15")
if (mProps.duration > 0 && (((meetingModel.startedOn + mProps.duration) - now) < 15)) {
// log.warning("MEETING WILL END IN 15 MINUTES!!!!")
}
}
def timeNowInMinutes(): Long = {
TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
}
def sendMeetingHasEnded(userId: String) {
outGW.send(new MeetingHasEnded(mProps.meetingID, userId))
outGW.send(new DisconnectUser(mProps.meetingID, userId))
}
private def handleEndMeeting(msg: EndMeeting) {
meetingModel.meetingHasEnded
outGW.send(new MeetingEnded(msg.meetingID, mProps.recorded, mProps.voiceBridge))
outGW.send(new DisconnectAllUsers(msg.meetingID))
}
private def handleVoiceConfRecordingStartedMessage(msg: VoiceConfRecordingStartedMessage) {
if (msg.recording) {
meetingModel.setVoiceRecordingFilename(msg.recordStream)
outGW.send(new VoiceRecordingStarted(mProps.meetingID, mProps.recorded, msg.recordStream, msg.timestamp, mProps.voiceBridge))
} else {
meetingModel.setVoiceRecordingFilename("")
outGW.send(new VoiceRecordingStopped(mProps.meetingID, mProps.recorded, msg.recordStream, msg.timestamp, mProps.voiceBridge))
}
}
private def handleSetRecordingStatus(msg: SetRecordingStatus) {
log.info("Change recording status. meetingId=" + mProps.meetingID + " recording=" + msg.recording)
if (mProps.allowStartStopRecording && meetingModel.isRecording() != msg.recording) {
if (msg.recording) {
meetingModel.recordingStarted()
} else {
meetingModel.recordingStopped()
}
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, msg.userId, msg.recording))
}
}
private def handleGetRecordingStatus(msg: GetRecordingStatus) {
outGW.send(new GetRecordingStatusReply(mProps.meetingID, mProps.recorded, msg.userId, meetingModel.isRecording().booleanValue()))
}
def lockLayout(lock: Boolean) {
meetingModel.lockLayout(lock)
}
def newPermissions(np: Permissions) {
meetingModel.setPermissions(np)
}
def permissionsEqual(other: Permissions): Boolean = {
meetingModel.permissionsEqual(other)
}
// Broadcast video stream,
private def handleDeskShareStartedRequest(msg: DeskShareStartedRequest) {
log.info("handleDeskShareStartedRequest: dsStarted=" + meetingModel.getDeskShareStarted())

View File

@ -5,8 +5,13 @@ import java.util.concurrent.TimeUnit
case object StopMeetingActor
case class MeetingProperties(meetingID: String, externalMeetingID: String, meetingName: String, recorded: Boolean,
voiceBridge: String, duration: Long, autoStartRecording: Boolean, allowStartStopRecording: Boolean,
moderatorPass: String, viewerPass: String, createTime: Long, createDate: String, red5DeskShareIP: String, red5DeskShareApp: String)
voiceBridge: String, duration: Int, autoStartRecording: Boolean, allowStartStopRecording: Boolean,
moderatorPass: String, viewerPass: String, createTime: Long, createDate: String,
red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean)
case class MeetingExtensionProp(maxExtensions: Int = 2, numExtensions: Int = 0, extendByMinutes: Int = 20,
sendNotice: Boolean = true, sent15MinNotice: Boolean = false,
sent10MinNotice: Boolean = false, sent5MinNotice: Boolean = false)
class MeetingModel {
private var audioSettingsInited = false
@ -18,7 +23,6 @@ class MeetingModel {
private var meetingEnded = false
private var meetingMuted = false
val TIMER_INTERVAL = 30000
private var hasLastWebUserLeft = false
private var lastWebUserLeftOnTimestamp: Long = 0
@ -28,7 +32,12 @@ class MeetingModel {
private var desktopShareVideoWidth = 0
private var desktopShareVideoHeight = 0
val startedOn = timeNowInMinutes;
private var extension = new MeetingExtensionProp
val startedOn = timeNowInSeconds;
var breakoutRoomsStartedOn: Long = 0;
var breakoutRoomsdurationInMinutes: Int = 0;
def resetDesktopSharingParams() = {
broadcastingRTMP = false
@ -63,30 +72,6 @@ class MeetingModel {
desktopShareVideoHeight
}
def muteMeeting() {
meetingMuted = true
}
def unmuteMeeting() {
meetingMuted = false
}
def isMeetingMuted(): Boolean = {
meetingMuted
}
def recordingStarted() {
recording = true
}
def recordingStopped() {
recording = false
}
def isRecording(): Boolean = {
recording
}
def broadcastingRTMPStarted() {
broadcastingRTMP = true
}
@ -99,26 +84,6 @@ class MeetingModel {
broadcastingRTMP = false
}
def lastWebUserLeft() {
lastWebUserLeftOnTimestamp = timeNowInMinutes
}
def lastWebUserLeftOn(): Long = {
lastWebUserLeftOnTimestamp
}
def resetLastWebUserLeftOn() {
lastWebUserLeftOnTimestamp = 0
}
def setVoiceRecordingFilename(path: String) {
voiceRecordingFilename = path
}
def getVoiceRecordingFilename(): String = {
voiceRecordingFilename
}
def setRTMPBroadcastingUrl(path: String) {
println("---RTMP broadcastUrl changed to:" + path)
rtmpBroadcastingUrl = path
@ -128,47 +93,40 @@ class MeetingModel {
rtmpBroadcastingUrl
}
def permisionsInitialized(): Boolean = {
permissionsInited
def isExtensionAllowed(): Boolean = extension.numExtensions < extension.maxExtensions
def incNumExtension(): Int = {
if (extension.numExtensions < extension.maxExtensions) {
extension = extension.copy(numExtensions = extension.numExtensions + 1); extension.numExtensions
}
extension.numExtensions
}
def initializePermissions() {
permissionsInited = true
}
def notice15MinutesSent() = extension = extension.copy(sent15MinNotice = true)
def notice10MinutesSent() = extension = extension.copy(sent10MinNotice = true)
def notice5MinutesSent() = extension = extension.copy(sent5MinNotice = true)
def audioSettingsInitialized(): Boolean = {
audioSettingsInited
}
def initializeAudioSettings() {
audioSettingsInited = true
}
def permissionsEqual(other: Permissions): Boolean = {
permissions == other
}
def lockLayout(lock: Boolean) {
permissions = permissions.copy(lockedLayout = lock)
}
def getPermissions(): Permissions = {
permissions
}
def setPermissions(p: Permissions) {
permissions = p
}
def meetingHasEnded() {
meetingEnded = true
}
def hasMeetingEnded(): Boolean = {
meetingEnded
}
def timeNowInMinutes(): Long = {
TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
}
}
def getMeetingExtensionProp(): MeetingExtensionProp = extension
def muteMeeting() = meetingMuted = true
def unmuteMeeting() = meetingMuted = false
def isMeetingMuted(): Boolean = meetingMuted
def recordingStarted() = recording = true
def recordingStopped() = recording = false
def isRecording(): Boolean = recording
def lastWebUserLeft() = lastWebUserLeftOnTimestamp = timeNowInMinutes
def lastWebUserLeftOn(): Long = lastWebUserLeftOnTimestamp
def resetLastWebUserLeftOn() = lastWebUserLeftOnTimestamp = 0
def setVoiceRecordingFilename(path: String) = voiceRecordingFilename = path
def getVoiceRecordingFilename(): String = voiceRecordingFilename
def permisionsInitialized(): Boolean = permissionsInited
def initializePermissions() = permissionsInited = true
def audioSettingsInitialized(): Boolean = audioSettingsInited
def initializeAudioSettings() = audioSettingsInited = true
def permissionsEqual(other: Permissions): Boolean = permissions == other
def lockLayout(lock: Boolean) = permissions = permissions.copy(lockedLayout = lock)
def getPermissions(): Permissions = permissions
def setPermissions(p: Permissions) = permissions = p
def meetingHasEnded() = meetingEnded = true
def hasMeetingEnded(): Boolean = meetingEnded
def timeNowInMinutes(): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
def timeNowInSeconds(): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
}

View File

@ -11,6 +11,7 @@ import org.bigbluebutton.common.messages.StartRecordingVoiceConfRequestMessage
import org.bigbluebutton.common.messages.StopRecordingVoiceConfRequestMessage
import org.bigbluebutton.core.pubsub.senders.MeetingMessageToJsonConverter
import org.bigbluebutton.core.pubsub.senders.PesentationMessageToJsonConverter
import org.bigbluebutton.core.pubsub.senders.CaptionMessageToJsonConverter
import org.bigbluebutton.core.pubsub.senders.DeskShareMessageToJsonConverter
import org.bigbluebutton.common.messages.GetPresentationInfoReplyMessage
import org.bigbluebutton.common.messages.PresentationRemovedMessage
@ -29,13 +30,14 @@ import org.bigbluebutton.common.messages.UserEjectedFromMeetingMessage
import org.bigbluebutton.common.messages.LockLayoutMessage
import org.bigbluebutton.core.pubsub.senders.WhiteboardMessageToJsonConverter
import org.bigbluebutton.common.converters.ToJsonEncoder
import org.bigbluebutton.common.messages.TransferUserToVoiceConfRequestMessage
object MessageSenderActor {
def props(meetingId: String, msgSender: MessageSender): Props =
Props(classOf[MessageSenderActor], meetingId, msgSender)
def props(msgSender: MessageSender): Props =
Props(classOf[MessageSenderActor], msgSender)
}
class MessageSenderActor(val meetingId: String, val service: MessageSender)
class MessageSenderActor(val service: MessageSender)
extends Actor with ActorLogging {
val encoder = new ToJsonEncoder()
@ -101,6 +103,7 @@ class MessageSenderActor(val meetingId: String, val service: MessageSender)
case msg: UserVoiceTalking => handleUserVoiceTalking(msg)
case msg: MuteVoiceUser => handleMuteVoiceUser(msg)
case msg: EjectVoiceUser => handleEjectVoiceUser(msg)
case msg: TransferUserToMeeting => handleTransferUserToMeeting(msg)
case msg: GetUsersInVoiceConference => handleGetUsersFromVoiceConference(msg)
case msg: UserJoinedVoice => handleUserJoinedVoice(msg)
case msg: UserLeftVoice => handleUserLeftVoice(msg)
@ -115,6 +118,18 @@ class MessageSenderActor(val meetingId: String, val service: MessageSender)
case msg: UndoWhiteboardEvent => handleUndoWhiteboardEvent(msg)
case msg: WhiteboardEnabledEvent => handleWhiteboardEnabledEvent(msg)
case msg: IsWhiteboardEnabledReply => handleIsWhiteboardEnabledReply(msg)
// breakout room cases
case msg: BreakoutRoomsListOutMessage => handleBreakoutRoomsListOutMessage(msg)
case msg: BreakoutRoomStartedOutMessage => handleBreakoutRoomStartedOutMessage(msg)
case msg: BreakoutRoomEndedOutMessage => handleBreakoutRoomEndedOutMessage(msg)
case msg: BreakoutRoomJoinURLOutMessage => handleBreakoutRoomJoinURLOutMessage(msg)
case msg: UpdateBreakoutUsersOutMessage => handleUpdateBreakoutUsersOutMessage(msg)
case msg: MeetingTimeRemainingUpdate => handleMeetingTimeRemainingUpdate(msg)
case msg: BreakoutRoomsTimeRemainingUpdateOutMessage => handleBreakoutRoomsTimeRemainingUpdate(msg)
case msg: SendCaptionHistoryReply => handleSendCaptionHistoryReply(msg)
case msg: UpdateCaptionOwnerReply => handleUpdateCaptionOwnerReply(msg)
case msg: EditCaptionHistoryReply => handleEditCaptionHistoryReply(msg)
case msg: DeskShareStartRTMPBroadcast => handleDeskShareStartRTMPBroadcast(msg)
case msg: DeskShareStopRTMPBroadcast => handleDeskShareStopRTMPBroadcast(msg)
case msg: DeskShareNotifyViewersRTMP => handleDeskShareNotifyViewersRTMP(msg)
@ -599,7 +614,11 @@ class MessageSenderActor(val meetingId: String, val service: MessageSender)
private def handleEjectVoiceUser(msg: EjectVoiceUser) {
val m = new EjectUserFromVoiceConfRequestMessage(msg.meetingID, msg.voiceConfId, msg.voiceUserId)
service.send(MessagingConstants.TO_VOICE_CONF_SYSTEM_CHAN, m.toJson())
}
private def handleTransferUserToMeeting(msg: TransferUserToMeeting) {
val m = new TransferUserToVoiceConfRequestMessage(msg.voiceConfId, msg.targetVoiceConfId, msg.userId);
service.send(MessagingConstants.TO_VOICE_CONF_SYSTEM_CHAN, m.toJson())
}
private def handleUserLeftVoice(msg: UserLeftVoice) {
@ -613,12 +632,14 @@ class MessageSenderActor(val meetingId: String, val service: MessageSender)
}
private def handleValidateAuthTokenReply(msg: ValidateAuthTokenReply) {
println("**** handleValidateAuthTokenReply *****")
val json = UsersMessageToJsonConverter.validateAuthTokenReplyToJson(msg)
//println("************** Publishing [" + json + "] *******************")
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
}
private def handleValidateAuthTokenTimedOut(msg: ValidateAuthTokenTimedOut) {
println("**** handleValidateAuthTokenTimedOut *****")
val json = UsersMessageToJsonConverter.validateAuthTokenTimeoutToJson(msg)
//println("************** Publishing [" + json + "] *******************")
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
@ -683,4 +704,56 @@ class MessageSenderActor(val meetingId: String, val service: MessageSender)
val json = WhiteboardMessageToJsonConverter.isWhiteboardEnabledReplyToJson(msg)
service.send(MessagingConstants.FROM_WHITEBOARD_CHANNEL, json)
}
private def handleBreakoutRoomsListOutMessage(msg: BreakoutRoomsListOutMessage) {
val json = MeetingMessageToJsonConverter.breakoutRoomsListOutMessageToJson(msg)
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
}
private def handleBreakoutRoomStartedOutMessage(msg: BreakoutRoomStartedOutMessage) {
val json = MeetingMessageToJsonConverter.breakoutRoomStartedOutMessageToJson(msg)
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
}
private def handleBreakoutRoomEndedOutMessage(msg: BreakoutRoomEndedOutMessage) {
val json = MeetingMessageToJsonConverter.breakoutRoomEndedOutMessageToJson(msg)
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
}
private def handleBreakoutRoomJoinURLOutMessage(msg: BreakoutRoomJoinURLOutMessage) {
val json = MeetingMessageToJsonConverter.breakoutRoomJoinURLOutMessageToJson(msg)
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
}
private def handleUpdateBreakoutUsersOutMessage(msg: UpdateBreakoutUsersOutMessage) {
val json = MeetingMessageToJsonConverter.updateBreakoutUsersOutMessageToJson(msg)
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
}
private def handleMeetingTimeRemainingUpdate(msg: MeetingTimeRemainingUpdate) {
val json = MeetingMessageToJsonConverter.meetingTimeRemainingUpdateToJson(msg)
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
}
private def handleSendCaptionHistoryReply(msg: SendCaptionHistoryReply) {
val json = CaptionMessageToJsonConverter.sendCaptionHistoryReplyToJson(msg)
service.send(MessagingConstants.FROM_CAPTION_CHANNEL, json)
}
private def handleUpdateCaptionOwnerReply(msg: UpdateCaptionOwnerReply) {
val json = CaptionMessageToJsonConverter.updateCaptionOwnerReplyToJson(msg)
service.send(MessagingConstants.FROM_CAPTION_CHANNEL, json)
}
private def handleEditCaptionHistoryReply(msg: EditCaptionHistoryReply) {
println("handleEditCaptionHistoryReply")
val json = CaptionMessageToJsonConverter.editCaptionHistoryReplyToJson(msg)
println(json)
service.send(MessagingConstants.FROM_CAPTION_CHANNEL, json)
}
private def handleBreakoutRoomsTimeRemainingUpdate(msg: BreakoutRoomsTimeRemainingUpdateOutMessage) {
val json = MeetingMessageToJsonConverter.breakoutRoomsTimeRemainingUpdateToJson(msg)
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
}
}

View File

@ -2,20 +2,18 @@ package org.bigbluebutton.core
import akka.actor.ActorRef
import akka.actor.ActorContext
import org.bigbluebutton.core.api.MessageOutGateway
import org.bigbluebutton.core.service.recorder.RecorderApplication
import org.bigbluebutton.core.bus.OutgoingEventBus
import org.bigbluebutton.core.bus.BigBlueButtonOutMessage
import org.bigbluebutton.core.api.IOutMessage
object OutMessageGateway {
def apply(meetingId: String, recorder: RecorderApplication, sender: MessageSender)(implicit context: ActorContext) =
new OutMessageGateway(meetingId, recorder, sender)(context)
def apply(outgoingEventBus: OutgoingEventBus) =
new OutMessageGateway(outgoingEventBus)
}
class OutMessageGateway(val meetingId: String, val recorder: RecorderApplication, val sender: MessageSender)(implicit val context: ActorContext) {
private val outGW = context.actorOf(OutMessageGatewayActor.props(meetingId, recorder, sender), meetingId)
class OutMessageGateway(outgoingEventBus: OutgoingEventBus) {
def send(msg: IOutMessage) {
outGW forward msg
outgoingEventBus.publish(BigBlueButtonOutMessage("outgoingMessageChannel", msg))
}
}

View File

@ -8,8 +8,8 @@ import org.bigbluebutton.core.api._
import java.util.concurrent.TimeUnit
import org.bigbluebutton.core.util._
import scala.concurrent.duration._
import org.bigbluebutton.core.apps.{ PollApp, UsersApp, PresentationApp, LayoutApp, ChatApp, WhiteboardApp }
import org.bigbluebutton.core.apps.{ ChatModel, LayoutModel, UsersModel, PollModel, WhiteboardModel }
import org.bigbluebutton.core.apps.{ PollApp, UsersApp, PresentationApp, LayoutApp, ChatApp, WhiteboardApp, CaptionApp }
import org.bigbluebutton.core.apps.{ ChatModel, LayoutModel, UsersModel, PollModel, WhiteboardModel, CaptionModel }
import org.bigbluebutton.core.apps.PresentationModel
import org.bigbluebutton.core.service.recorder.RecorderApplication
@ -21,8 +21,8 @@ object OutMessageGatewayActor {
class OutMessageGatewayActor(val meetingId: String, val recorder: RecorderApplication, val msgSender: MessageSender)
extends Actor with ActorLogging {
private val recorderActor = context.actorOf(RecorderActor.props(meetingId, recorder), "recorderActor-" + meetingId)
private val msgSenderActor = context.actorOf(MessageSenderActor.props(meetingId, msgSender), "senderActor-" + meetingId)
private val recorderActor = context.actorOf(RecorderActor.props(recorder), "recorderActor-" + meetingId)
private val msgSenderActor = context.actorOf(MessageSenderActor.props(msgSender), "senderActor-" + meetingId)
def receive = {
case msg: IOutMessage => {

View File

@ -0,0 +1,40 @@
package org.bigbluebutton.core
import spray.json.{ DefaultJsonProtocol, JsValue, JsString, DeserializationException, JsonFormat }
import org.bigbluebutton.core.api._
object UserMessagesProtocol extends DefaultJsonProtocol {
/*
implicit object RoleJsonFormat extends JsonFormat[Role.RoleType] {
def write(obj: Role.RoleType): JsValue = JsString(obj.toString)
def read(json: JsValue): Role.RoleType = json match {
case JsString(str) => Role.withName(str)
case _ => throw new DeserializationException("Enum string expected")
}
}
*/
implicit object MessageTypeFormat extends JsonFormat[MessageType.MessageType] {
def write(obj: MessageType.MessageType): JsValue = JsString(obj.toString)
def read(json: JsValue): MessageType.MessageType = json match {
case JsString(str) => MessageType.withName(str)
case _ => throw new DeserializationException("Enum string expected")
}
}
implicit val breakoutRoomInPayloadFormat = jsonFormat2(BreakoutRoomInPayload)
implicit val createBreakoutRoomsFormat = jsonFormat3(CreateBreakoutRooms)
implicit val breakoutRoomsListMessageFormat = jsonFormat1(BreakoutRoomsListMessage)
implicit val requestBreakoutJoinURLInMessageFormat = jsonFormat3(RequestBreakoutJoinURLInMessage)
implicit val transferUserToMeetingRequestFormat = jsonFormat3(TransferUserToMeetingRequest)
implicit val endBreakoutRoomsFormat = jsonFormat1(EndAllBreakoutRooms)
implicit val inMsgHeaderFormat = jsonFormat1(InMessageHeader)
implicit val outMsgHeaderFormat = jsonFormat1(OutMsgHeader)
implicit val outMsgEnvelopeHeaderFormat = jsonFormat2(OutMsgEnvelopeHeader)
implicit val createBreakoutRoomOutMsgPayloadFormat = jsonFormat8(CreateBreakoutRoomOutMsgPayload)
implicit val createBreakoutRoomOutMsgEnvelopePayloadFormat = jsonFormat2(CreateBreakoutRoomOutMsgEnvelopePayload)
implicit val createBreakoutRoomOutMsgEnvelopeFormat = jsonFormat2(CreateBreakoutRoomOutMsgEnvelope)
}

View File

@ -0,0 +1,16 @@
package org.bigbluebutton.core
case class OutMsgHeader(name: String)
case class OutMsgEnvelopeHeader(`type`: MessageType.MessageType, address: String)
trait OutMessage
case class CreateBreakoutRoomOutMsgEnvelope(header: OutMsgEnvelopeHeader,
payload: CreateBreakoutRoomOutMsgEnvelopePayload)
case class CreateBreakoutRoomOutMsgEnvelopePayload(header: OutMsgHeader,
payload: CreateBreakoutRoomOutMsgPayload)
case class CreateBreakoutRoomOutMsgPayload(breakoutId: String, name: String, parentId: String,
voiceConfId: String, durationInMinutes: Int,
moderatorPassword: String, viewerPassword: String,
defaultPresentationUrl: String)

View File

@ -35,14 +35,15 @@ import org.bigbluebutton.core.recorders.events.DeskShareNotifyViewersRTMPRecordE
// import org.bigbluebutton.core.service.whiteboard.WhiteboardKeyUtil
import org.bigbluebutton.common.messages.WhiteboardKeyUtil
import org.bigbluebutton.core.recorders.events.ModifyTextWhiteboardRecordEvent
import org.bigbluebutton.core.recorders.events.EditCaptionHistoryRecordEvent
import scala.collection.immutable.StringOps
object RecorderActor {
def props(meetingId: String, recorder: RecorderApplication): Props =
Props(classOf[RecorderActor], meetingId, recorder)
def props(recorder: RecorderApplication): Props =
Props(classOf[RecorderActor], recorder)
}
class RecorderActor(val meetingId: String, val recorder: RecorderApplication)
class RecorderActor(val recorder: RecorderApplication)
extends Actor with ActorLogging {
def receive = {
@ -71,6 +72,7 @@ class RecorderActor(val meetingId: String, val recorder: RecorderApplication)
case msg: SendWhiteboardAnnotationEvent => handleSendWhiteboardAnnotationEvent(msg)
case msg: ClearWhiteboardEvent => handleClearWhiteboardEvent(msg)
case msg: UndoWhiteboardEvent => handleUndoWhiteboardEvent(msg)
case msg: EditCaptionHistoryReply => handleEditCaptionHistoryReply(msg)
case msg: DeskShareStartRTMPBroadcast => handleDeskShareStartRTMPBroadcast(msg)
case msg: DeskShareStopRTMPBroadcast => handleDeskShareStopRTMPBroadcast(msg)
case msg: DeskShareNotifyViewersRTMP => handleDeskShareNotifyViewersRTMP(msg)
@ -442,6 +444,19 @@ class RecorderActor(val meetingId: String, val recorder: RecorderApplication)
}
private def handleEditCaptionHistoryReply(msg: EditCaptionHistoryReply) {
if (msg.recorded) {
val ev = new EditCaptionHistoryRecordEvent();
ev.setTimestamp(TimestampGenerator.generateTimestamp);
ev.setMeetingId(msg.meetingID);
ev.setStartIndex(msg.startIndex.toString());
ev.setEndIndex(msg.endIndex.toString());
ev.setLocale(msg.locale);
ev.setText(msg.text);
recorder.record(msg.meetingID, ev);
}
}
private def handleDeskShareStartRTMPBroadcast(msg: DeskShareStartRTMPBroadcast) {
val event = new DeskShareStartRTMPRecordEvent()
event.setMeetingId(msg.conferenceName)
@ -470,5 +485,4 @@ class RecorderActor(val meetingId: String, val recorder: RecorderApplication)
log.info("handleDeskShareNotifyViewersRTMP " + msg.meetingID)
recorder.record(msg.meetingID, event)
}
}
}

View File

@ -3,13 +3,17 @@ package org.bigbluebutton.core
import akka.actor.ActorRef
import akka.actor.ActorContext
import org.bigbluebutton.core.api.MessageOutGateway
import org.bigbluebutton.core.bus._
object RunningMeeting {
def apply(mProps: MeetingProperties, outGW: OutMessageGateway)(implicit context: ActorContext) =
new RunningMeeting(mProps, outGW)(context)
def apply(mProps: MeetingProperties, outGW: OutMessageGateway,
eventBus: IncomingEventBus)(implicit context: ActorContext) =
new RunningMeeting(mProps, outGW, eventBus)(context)
}
class RunningMeeting(val mProps: MeetingProperties, val outGW: OutMessageGateway)(implicit val context: ActorContext) {
class RunningMeeting(val mProps: MeetingProperties, val outGW: OutMessageGateway,
val eventBus: IncomingEventBus)(implicit val context: ActorContext) {
val actorRef = context.actorOf(MeetingActor.props(mProps, eventBus, outGW), mProps.meetingID)
val actorRef = context.actorOf(MeetingActor.props(mProps, outGW), mProps.meetingID)
}

View File

@ -4,27 +4,77 @@ import org.bigbluebutton.core.api.Role._
import org.bigbluebutton.core.apps.AnnotationVO
import org.bigbluebutton.core.apps.Presentation
import org.bigbluebutton.core.MeetingProperties
import org.bigbluebutton.core.apps.BreakoutUser
import spray.json.JsObject
trait InMessage { val meetingID: String }
case class InMessageHeader(name: String)
case class InHeaderAndJsonPayload(header: InMessageHeader, payload: JsObject)
case class MessageProcessException(message: String) extends Exception(message)
case class PubSubPing(system: String, timestamp: Long)
trait InMessage
case class IsMeetingActorAliveMessage(meetingId: String)
case class KeepAliveMessage(aliveID: String)
//////////////////////////////////////////////////////////////////////////////
// System
/////////////////////////////////////////////////////////////////////////////
case class PubSubPing(system: String, timestamp: Long) extends InMessage
case class IsMeetingActorAliveMessage(meetingId: String) extends InMessage
case class KeepAliveMessage(aliveID: String) extends InMessage
//////////////////////////////////////////////////////////////////////////////
// Meeting
/////////////////////////////////////////////////////////////////////////////
case class MonitorNumberOfUsers(meetingID: String) extends InMessage
case class SendTimeRemainingUpdate(meetingId: String) extends InMessage
case class ExtendMeetingDuration(meetingId: String, userId: String) extends InMessage
case class CreateMeeting(meetingID: String, mProps: MeetingProperties) extends InMessage
case class InitializeMeeting(meetingID: String, recorded: Boolean) extends InMessage
case class DestroyMeeting(meetingID: String) extends InMessage
case class StartMeeting(meetingID: String) extends InMessage
case class EndMeeting(meetingID: String) extends InMessage
case class EndMeeting(meetingId: String) extends InMessage
case class LockSetting(meetingID: String, locked: Boolean, settings: Map[String, Boolean]) extends InMessage
//////////////////////////////////////////////////////////////////////////////////////
// Breakout room
/////////////////////////////////////////////////////////////////////////////////////
// Sent by user to request the breakout rooms list of a room
case class BreakoutRoomsListMessage(meetingId: String) extends InMessage
// Sent by user to request creation of breakout rooms
case class CreateBreakoutRooms(meetingId: String, durationInMinutes: Int,
rooms: Vector[BreakoutRoomInPayload]) extends InMessage
case class BreakoutRoomInPayload(name: String, users: Vector[String])
// Sent by user to request for a join URL in order to be able to join a breakout room
case class RequestBreakoutJoinURLInMessage(meetingId: String, breakoutId: String,
userId: String) extends InMessage
// Sent by breakout actor to tell meeting actor that breakout room has been created.
case class BreakoutRoomCreated(meetingId: String, breakoutRoomId: String) extends InMessage
// Sent by breakout actor to tell meeting actor the list of users in the breakout room.
case class BreakoutRoomUsersUpdate(meetingId: String, breakoutId: String,
users: Vector[BreakoutUser]) extends InMessage
// Send by internal actor to tell the breakout actor to send it's list of users to the main meeting actor.
case class SendBreakoutUsersUpdate(meetingId: String) extends InMessage
// Sent by user to request ending all the breakout rooms
case class EndAllBreakoutRooms(meetingId: String) extends InMessage
// Sent by breakout actor to tell meeting actor that breakout room has been ended
case class BreakoutRoomEnded(meetingId: String, breakoutRoomId: String) extends InMessage
// Sent by user actor to ask for voice conference transfer
case class TransferUserToMeetingRequest(meetingId: String, targetMeetingId: String, userId: String) extends InMessage
////////////////////////////////////////////////////////////////////////////////////
// Lock
///////////////////////////////////////////////////////////////////////////////////
case class LockUser(meetingID: String, userId: String, lock: Boolean) extends InMessage
case class InitLockSettings(meetingID: String, settings: Permissions) extends InMessage
case class SetLockSettings(meetingID: String, setByUser: String, settings: Permissions) extends InMessage
case class GetLockSettings(meetingID: String, userId: String) extends InMessage
//////////////////////////////////////////////////////////////////////////////////
// Users
/////////////////////////////////////////////////////////////////////////////////
case class ValidateAuthToken(meetingID: String, userId: String, token: String,
correlationId: String, sessionId: String) extends InMessage
case class RegisterUser(meetingID: String, userID: String, name: String, role: Role,
@ -41,7 +91,10 @@ case class AssignPresenter(meetingID: String, newPresenterID: String, newPresent
case class SetRecordingStatus(meetingID: String, userId: String, recording: Boolean) extends InMessage
case class GetRecordingStatus(meetingID: String, userId: String) extends InMessage
//////////////////////////////////////////////////////////////////////////////////
// Chat
/////////////////////////////////////////////////////////////////////////////////
case class GetChatHistoryRequest(meetingID: String, requesterID: String, replyTo: String) extends InMessage
case class SendPublicMessageRequest(meetingID: String, requesterID: String, message: Map[String, String]) extends InMessage
case class SendPrivateMessageRequest(meetingID: String, requesterID: String, message: Map[String, String]) extends InMessage
@ -50,14 +103,20 @@ case class UserConnectedToGlobalAudio(meetingID: String, /** Not used. Just to s
case class UserDisconnectedFromGlobalAudio(meetingID: String, /** Not used. Just to satisfy trait **/ voiceConf: String,
userid: String, name: String) extends InMessage
///////////////////////////////////////////////////////////////////////////////////////
// Layout
//////////////////////////////////////////////////////////////////////////////////////
case class GetCurrentLayoutRequest(meetingID: String, requesterID: String) extends InMessage
case class SetLayoutRequest(meetingID: String, requesterID: String, layoutID: String) extends InMessage
case class LockLayoutRequest(meetingID: String, setById: String, lock: Boolean, viewersOnly: Boolean,
layout: Option[String]) extends InMessage
case class BroadcastLayoutRequest(meetingID: String, requesterID: String, layout: String) extends InMessage
//////////////////////////////////////////////////////////////////////////////////////
// Presentation
/////////////////////////////////////////////////////////////////////////////////////
case class ClearPresentation(meetingID: String) extends InMessage
case class RemovePresentation(meetingID: String, presentationID: String) extends InMessage
case class GetPresentationInfo(meetingID: String, requesterID: String, replyTo: String) extends InMessage
@ -77,7 +136,10 @@ case class PresentationSlideGenerated(meetingID: String, messageKey: String, cod
case class PresentationConversionCompleted(meetingID: String, messageKey: String, code: String,
presentation: Presentation) extends InMessage
/////////////////////////////////////////////////////////////////////////////////////
// Polling
////////////////////////////////////////////////////////////////////////////////////
//case class CreatePollRequest(meetingID: String, requesterId: String, pollId: String, pollType: String) extends InMessage
case class StartCustomPollRequest(meetingID: String, requesterId: String, pollType: String, answers: Seq[String]) extends InMessage
case class StartPollRequest(meetingID: String, requesterId: String, pollType: String) extends InMessage
@ -88,7 +150,10 @@ case class RespondToPollRequest(meetingID: String, requesterId: String, pollId:
case class GetPollRequest(meetingID: String, requesterId: String, pollId: String) extends InMessage
case class GetCurrentPollRequest(meetingID: String, requesterId: String) extends InMessage
///////////////////////////////////////////////////////////////////////////////////
// Voice
///////////////////////////////////////////////////////////////////////////////////
case class InitAudioSettings(meetingID: String, requesterID: String, muted: Boolean) extends InMessage
case class SendVoiceUsersRequest(meetingID: String, requesterID: String) extends InMessage
case class MuteAllExceptPresenterRequest(meetingID: String, requesterID: String, mute: Boolean) extends InMessage
@ -100,14 +165,17 @@ case class EjectUserFromVoiceRequest(meetingID: String, userId: String, ejectedB
case class VoiceUserJoinedMessage(meetingID: String, user: String, voiceConfId: String,
callerIdNum: String, callerIdName: String, muted: Boolean, talking: Boolean) extends InMessage
case class UserJoinedVoiceConfMessage(voiceConfId: String, voiceUserId: String, userId: String, externUserId: String,
callerIdName: String, callerIdNum: String, muted: Boolean, talking: Boolean, avatarURL: String, listenOnly: Boolean)
case class UserLeftVoiceConfMessage(voiceConfId: String, voiceUserId: String)
case class UserLockedInVoiceConfMessage(voiceConfId: String, voiceUserId: String, locked: Boolean)
case class UserMutedInVoiceConfMessage(voiceConfId: String, voiceUserId: String, muted: Boolean)
case class UserTalkingInVoiceConfMessage(voiceConfId: String, voiceUserId: String, talking: Boolean)
case class VoiceConfRecordingStartedMessage(voiceConfId: String, recordStream: String, recording: Boolean, timestamp: String)
callerIdName: String, callerIdNum: String, muted: Boolean, talking: Boolean, avatarURL: String, listenOnly: Boolean) extends InMessage
case class UserLeftVoiceConfMessage(voiceConfId: String, voiceUserId: String) extends InMessage
case class UserLockedInVoiceConfMessage(voiceConfId: String, voiceUserId: String, locked: Boolean) extends InMessage
case class UserMutedInVoiceConfMessage(voiceConfId: String, voiceUserId: String, muted: Boolean) extends InMessage
case class UserTalkingInVoiceConfMessage(voiceConfId: String, voiceUserId: String, talking: Boolean) extends InMessage
case class VoiceConfRecordingStartedMessage(voiceConfId: String, recordStream: String, recording: Boolean, timestamp: String) extends InMessage
/////////////////////////////////////////////////////////////////////////////////////
// Whiteboard
/////////////////////////////////////////////////////////////////////////////////////
case class SendWhiteboardAnnotationRequest(meetingID: String, requesterID: String, annotation: AnnotationVO) extends InMessage
case class GetWhiteboardShapesRequest(meetingID: String, requesterID: String, whiteboardId: String, replyTo: String) extends InMessage
case class ClearWhiteboardRequest(meetingID: String, requesterID: String, whiteboardId: String) extends InMessage
@ -116,10 +184,14 @@ case class EnableWhiteboardRequest(meetingID: String, requesterID: String, enabl
case class IsWhiteboardEnabledRequest(meetingID: String, requesterID: String, replyTo: String) extends InMessage
case class GetAllMeetingsRequest(meetingID: String /** Not used. Just to satisfy trait **/ ) extends InMessage
// Caption
case class SendCaptionHistoryRequest(meetingID: String, requesterID: String) extends InMessage
case class UpdateCaptionOwnerRequest(meetingID: String, locale: String, ownerID: String) extends InMessage
case class EditCaptionHistoryRequest(meetingID: String, userID: String, startIndex: Integer, endIndex: Integer, locale: String, text: String) extends InMessage
// DeskShare
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String)
case class DeskShareStoppedRequest(conferenceName: String, callerId: String, callerIdName: String)
case class DeskShareRTMPBroadcastStartedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String)
case class DeskShareRTMPBroadcastStoppedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String)
case class DeskShareGetDeskShareInfoRequest(conferenceName: String, requesterID: String, replyTo: String)
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
case class DeskShareStoppedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
case class DeskShareRTMPBroadcastStartedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
case class DeskShareRTMPBroadcastStoppedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
case class DeskShareGetDeskShareInfoRequest(conferenceName: String, requesterID: String, replyTo: String) extends InMessage

View File

@ -162,5 +162,11 @@ object MessageNames {
val MEETING_DESTROYED_EVENT = "meeting_destroyed_event"
val KEEP_ALIVE_REPLY = "keep_alive_reply"
val USER_LISTEN_ONLY = "user_listening_only"
val GET_ALL_MEETINGS_REPLY = "get_all_meetings_reply"
}
val GET_ALL_MEETINGS_REPLY = "get_all_meetings_reply_message"
val EDIT_CAPTION_HISTORY = "edit_caption_history_message"
val UPDATE_CAPTION_OWNER = "update_caption_owner_message"
val SEND_CAPTION_HISTORY_REPLY = "send_caption_history_reply_message"
// breakout rooms
val BREAKOUT_ROOM_STARTED = "BreakoutRoomStarted"
}

View File

@ -8,15 +8,14 @@ import org.bigbluebutton.core.MeetingProperties
import org.bigbluebutton.core.apps.PollVO
import org.bigbluebutton.core.apps.SimplePollOutVO
import org.bigbluebutton.core.apps.SimplePollResultOutVO
abstract class OutMessage
import org.bigbluebutton.core.apps.BreakoutUser
case class VoiceRecordingStarted(meetingID: String, recorded: Boolean, recordingFile: String, timestamp: String, confNum: String) extends IOutMessage
case class VoiceRecordingStopped(meetingID: String, recorded: Boolean, recordingFile: String, timestamp: String, confNum: String) extends IOutMessage
case class RecordingStatusChanged(meetingID: String, recorded: Boolean, userId: String, recording: Boolean) extends IOutMessage
case class GetRecordingStatusReply(meetingID: String, recorded: Boolean, userId: String, recording: Boolean) extends IOutMessage
case class MeetingCreated(meetingID: String, externalMeetingID: String, recorded: Boolean, name: String,
voiceBridge: String, duration: Long, moderatorPass: String, viewerPass: String, createTime: Long, createDate: String) extends IOutMessage
voiceBridge: String, duration: Int, moderatorPass: String, viewerPass: String, createTime: Long, createDate: String) extends IOutMessage
case class MeetingMuted(meetingID: String, recorded: Boolean, meetingMuted: Boolean) extends IOutMessage
case class MeetingEnded(meetingID: String, recorded: Boolean, voiceBridge: String) extends IOutMessage
case class MeetingState(meetingID: String, recorded: Boolean, userId: String, permissions: Permissions, meetingMuted: Boolean) extends IOutMessage
@ -28,6 +27,21 @@ case class KeepAliveMessageReply(aliveID: String) extends IOutMessage
case class PubSubPong(system: String, timestamp: Long) extends IOutMessage
case object IsAliveMessage extends IOutMessage
// Breakout Rooms
case class BreakoutRoomsListOutMessage(meetingId: String, rooms: Vector[BreakoutRoomBody]) extends IOutMessage
case class CreateBreakoutRoom(meetingId: String, recorded: Boolean, room: BreakoutRoomOutPayload) extends IOutMessage
case class EndBreakoutRoom(breakoutId: String) extends IOutMessage
case class BreakoutRoomOutPayload(breakoutId: String, name: String, parentId: String,
voiceConfId: String, durationInMinutes: Int, moderatorPassword: String, viewerPassword: String,
defaultPresentationURL: String)
case class BreakoutRoomJoinURLOutMessage(meetingId: String, recorded: Boolean, breakoutId: String, userId: String, joinURL: String) extends IOutMessage
case class BreakoutRoomStartedOutMessage(meetingId: String, recorded: Boolean, breakout: BreakoutRoomBody) extends IOutMessage
case class BreakoutRoomBody(name: String, breakoutId: String)
case class UpdateBreakoutUsersOutMessage(meetingId: String, recorded: Boolean, breakoutId: String, users: Vector[BreakoutUser]) extends IOutMessage
case class MeetingTimeRemainingUpdate(meetingId: String, recorded: Boolean, timeRemaining: Int) extends IOutMessage
case class BreakoutRoomsTimeRemainingUpdateOutMessage(meetingId: String, recorded: Boolean, timeRemaining: Int) extends IOutMessage
case class BreakoutRoomEndedOutMessage(meetingId: String, breakoutId: String) extends IOutMessage
// Permissions
case class PermissionsSettingInitialized(meetingID: String, permissions: Permissions, applyTo: Array[UserVO]) extends IOutMessage
case class NewPermissionsSetting(meetingID: String, setByUser: String, permissions: Permissions, applyTo: Array[UserVO]) extends IOutMessage
@ -56,6 +70,7 @@ case class MuteVoiceUser(meetingID: String, recorded: Boolean, requesterID: Stri
case class UserVoiceMuted(meetingID: String, recorded: Boolean, confNum: String, user: UserVO) extends IOutMessage
case class UserVoiceTalking(meetingID: String, recorded: Boolean, confNum: String, user: UserVO) extends IOutMessage
case class EjectVoiceUser(meetingID: String, recorded: Boolean, requesterID: String, userId: String, voiceConfId: String, voiceUserId: String) extends IOutMessage
case class TransferUserToMeeting(voiceConfId: String, targetVoiceConfId: String, userId: String) extends IOutMessage
case class UserJoinedVoice(meetingID: String, recorded: Boolean, confNum: String, user: UserVO) extends IOutMessage
case class UserLeftVoice(meetingID: String, recorded: Boolean, confNum: String, user: UserVO) extends IOutMessage
@ -130,6 +145,10 @@ case class WhiteboardEnabledEvent(meetingID: String, recorded: Boolean, requeste
case class IsWhiteboardEnabledReply(meetingID: String, recorded: Boolean, requesterID: String, enabled: Boolean, replyTo: String) extends IOutMessage
case class GetAllMeetingsReply(meetings: Array[MeetingInfo]) extends IOutMessage
// Chat
case class SendCaptionHistoryReply(meetingID: String, recorded: Boolean, requesterID: String, history: Map[String, Array[String]]) extends IOutMessage
case class UpdateCaptionOwnerReply(meetingID: String, recorded: Boolean, locale: String, ownerID: String) extends IOutMessage
case class EditCaptionHistoryReply(meetingID: String, recorded: Boolean, userID: String, startIndex: Integer, endIndex: Integer, locale: String, text: String) extends IOutMessage
// DeskShare
case class DeskShareStartRTMPBroadcast(conferenceName: String, streamPath: String) extends IOutMessage
case class DeskShareStopRTMPBroadcast(conferenceName: String, streamPath: String) extends IOutMessage

View File

@ -0,0 +1,227 @@
package org.bigbluebutton.core.apps
import org.bigbluebutton.core.api._
import scala.collection.mutable.ArrayBuffer
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.SystemConfiguration
import org.apache.commons.codec.digest.DigestUtils
import scala.collection._
import scala.collection.SortedSet
import org.apache.commons.lang3.StringUtils
import java.net.URLEncoder
import scala.collection.immutable.StringOps
import org.bigbluebutton.core.LiveMeeting
import org.bigbluebutton.core.bus.IncomingEventBus
import org.bigbluebutton.core.bus.BigBlueButtonEvent
trait BreakoutRoomApp extends SystemConfiguration {
this: LiveMeeting =>
val outGW: OutMessageGateway
val eventBus: IncomingEventBus
def getDefaultPresentationURL(): String = {
var presURL = bbbWebDefaultPresentationURL
val page = presModel.getCurrentPage()
page foreach { p =>
presURL = BreakoutRoomsUtil.fromSWFtoPDF(p.swfUri)
}
presURL
}
def handleBreakoutRoomsList(msg: BreakoutRoomsListMessage) {
val breakoutRooms = breakoutModel.getRooms().toVector map { r => new BreakoutRoomBody(r.name, r.id) }
outGW.send(new BreakoutRoomsListOutMessage(mProps.meetingID, breakoutRooms));
}
def handleCreateBreakoutRooms(msg: CreateBreakoutRooms) {
var i = 0
for (room <- msg.rooms) {
i += 1
val presURL = bbbWebDefaultPresentationURL
val breakoutMeetingId = BreakoutRoomsUtil.createMeetingId(mProps.meetingID, i)
val voiceConfId = BreakoutRoomsUtil.createVoiceConfId(mProps.voiceBridge, i)
val r = breakoutModel.createBreakoutRoom(breakoutMeetingId, room.name, voiceConfId, room.users, presURL)
val p = new BreakoutRoomOutPayload(r.id, r.name, mProps.meetingID,
r.voiceConfId, msg.durationInMinutes, bbbWebModeratorPassword, bbbWebViewerPassword,
r.defaultPresentationURL)
outGW.send(new CreateBreakoutRoom(mProps.meetingID, mProps.recorded, p))
}
meetingModel.breakoutRoomsdurationInMinutes = msg.durationInMinutes;
meetingModel.breakoutRoomsStartedOn = timeNowInSeconds;
}
def sendJoinURL(userId: String, breakoutId: String) {
for {
user <- usersModel.getUser(userId)
apiCall = "join"
params = BreakoutRoomsUtil.joinParams(user.name, userId, true, breakoutId, bbbWebModeratorPassword, true)
baseString = BreakoutRoomsUtil.createBaseString(params)
checksum = BreakoutRoomsUtil.calculateChecksum(apiCall, baseString, bbbWebSharedSecret)
joinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, baseString, checksum)
} yield outGW.send(new BreakoutRoomJoinURLOutMessage(mProps.meetingID, mProps.recorded, breakoutId, userId, joinURL))
}
def handleRequestBreakoutJoinURL(msg: RequestBreakoutJoinURLInMessage) {
sendJoinURL(msg.userId, msg.breakoutId)
}
def handleBreakoutRoomCreated(msg: BreakoutRoomCreated) {
val room = breakoutModel.getBreakoutRoom(msg.breakoutRoomId)
room foreach { room =>
sendBreakoutRoomStarted(mProps.meetingID, room.name, room.id, room.voiceConfId)
}
breakoutModel.getAssignedUsers(msg.breakoutRoomId) foreach { users =>
users.foreach { u =>
log.debug("## Sending Join URL for users: {}", u);
sendJoinURL(u, msg.breakoutRoomId)
}
}
}
def sendBreakoutRoomStarted(meetingId: String, breakoutName: String, breakoutId: String, voiceConfId: String) {
outGW.send(new BreakoutRoomStartedOutMessage(meetingId, mProps.recorded, new BreakoutRoomBody(breakoutName, breakoutId)))
}
def handleBreakoutRoomEnded(msg: BreakoutRoomEnded) {
breakoutModel.remove(msg.breakoutRoomId)
outGW.send(new BreakoutRoomEndedOutMessage(msg.meetingId, msg.breakoutRoomId))
}
def handleBreakoutRoomUsersUpdate(msg: BreakoutRoomUsersUpdate) {
breakoutModel.updateBreakoutUsers(msg.breakoutId, msg.users) foreach { room =>
outGW.send(new UpdateBreakoutUsersOutMessage(mProps.meetingID, mProps.recorded, msg.breakoutId, room.users))
}
}
def handleSendBreakoutUsersUpdate(msg: SendBreakoutUsersUpdate) {
val users = usersModel.getUsers().toVector
val breakoutUsers = users map { u => new BreakoutUser(u.externUserID, u.name) }
eventBus.publish(BigBlueButtonEvent(mProps.externalMeetingID,
new BreakoutRoomUsersUpdate(mProps.externalMeetingID, mProps.meetingID, breakoutUsers)))
}
def handleTransferUserToMeeting(msg: TransferUserToMeetingRequest) {
var targetVoiceBridge: String = msg.targetMeetingId
// If the current room is a parent room we fetch the voice bridge from the breakout room
if (!mProps.isBreakout) {
breakoutModel.getBreakoutRoom(msg.targetMeetingId) match {
case Some(b) => {
targetVoiceBridge = b.voiceConfId;
}
case None => // do nothing
}
} // if it is a breakout room, the target voice bridge is the same after removing the last digit
else {
targetVoiceBridge = mProps.voiceBridge.dropRight(1)
}
// We check the iser from the mode
usersModel.getUser(msg.userId) match {
case Some(u) => {
if (u.voiceUser.joined) {
log.info("Transferring user userId=" + u.userID + " from voiceBridge=" + mProps.voiceBridge + " to targetVoiceConf=" + targetVoiceBridge)
outGW.send(new TransferUserToMeeting(mProps.voiceBridge, targetVoiceBridge, u.voiceUser.userId))
}
}
case None => // do nothing
}
}
def handleEndAllBreakoutRooms(msg: EndAllBreakoutRooms) {
log.info("EndAllBreakoutRooms event received for meetingId={}", mProps.meetingID)
breakoutModel.getRooms().foreach { room =>
outGW.send(new EndBreakoutRoom(room.id))
}
}
}
object BreakoutRoomsUtil {
def createMeetingId(id: String, index: Int): String = {
id.concat("-").concat(index.toString())
}
def createVoiceConfId(id: String, index: Int): String = {
id.concat(index.toString())
}
def fromSWFtoPDF(swfURL: String): String = {
swfURL.replace("swf", "pdf")
}
def createJoinURL(webAPI: String, apiCall: String, baseString: String, checksum: String): String = {
var apiURL = if (webAPI.endsWith("/")) webAPI else webAPI.concat("/")
apiURL.concat(apiCall).concat("?").concat(baseString).concat("&checksum=").concat(checksum)
}
//
//checksum() -- Return a checksum based on SHA-1 digest
//
def checksum(s: String): String = {
DigestUtils.sha1Hex(s);
}
def calculateChecksum(apiCall: String, baseString: String, sharedSecret: String): String = {
checksum(apiCall.concat(baseString).concat(sharedSecret))
}
def joinParams(username: String, userId: String, isBreakout: Boolean, breakoutId: String,
password: String, redirect: Boolean): mutable.Map[String, String] = {
val params = new collection.mutable.HashMap[String, String]
params += "fullName" -> urlEncode(username)
params += "userID" -> urlEncode(userId + "-" + breakoutId.substring(breakoutId.lastIndexOf("-") + 1));
params += "isBreakout" -> urlEncode(isBreakout.toString())
params += "meetingID" -> urlEncode(breakoutId)
params += "password" -> urlEncode(password)
params += "redirect" -> urlEncode(redirect.toString())
params
}
def sortParams(params: mutable.Map[String, String]): SortedSet[String] = {
collection.immutable.SortedSet[String]() ++ params.keySet
}
//From the list of parameters we want to pass. Creates a base string with parameters
//sorted in alphabetical order for us to sign.
def createBaseString(params: mutable.Map[String, String]): String = {
val csbuf = new StringBuffer()
val keys = sortParams(params)
var first = true;
for (key <- keys) {
for (value <- params.get(key)) {
if (first) {
first = false;
} else {
csbuf.append("&");
}
csbuf.append(key);
csbuf.append("=");
csbuf.append(value);
}
}
return csbuf.toString();
}
def urlEncode(s: String): String = {
URLEncoder.encode(s, "UTF-8");
}
//
//encodeURIComponent() -- Java encoding similiar to JavaScript encodeURIComponent
//
def encodeURIComponent(component: String): String = {
URLEncoder.encode(component, "UTF-8")
.replaceAll("\\%28", "(")
.replaceAll("\\%29", ")")
.replaceAll("\\+", "%20")
.replaceAll("\\%27", "'")
.replaceAll("\\%21", "!")
.replaceAll("\\%7E", "~")
}
}

View File

@ -0,0 +1,50 @@
package org.bigbluebutton.core.apps
import scala.collection.mutable.ArrayBuffer
import scala.collection.immutable.HashMap
case class BreakoutUser(id: String, name: String)
case class BreakoutRoom(id: String, name: String, voiceConfId: String,
assignedUsers: Vector[String], users: Vector[BreakoutUser], defaultPresentationURL: String)
class BreakoutRoomModel {
private var rooms = new collection.immutable.HashMap[String, BreakoutRoom]
def add(room: BreakoutRoom): BreakoutRoom = {
rooms += room.id -> room
room
}
def remove(id: String) {
rooms -= id
}
def createBreakoutRoom(id: String, name: String, voiceConfId: String,
assignedUsers: Vector[String], defaultPresentationURL: String): BreakoutRoom = {
val room = new BreakoutRoom(id, name, voiceConfId, assignedUsers, Vector(), defaultPresentationURL)
add(room)
}
def getBreakoutRoom(id: String): Option[BreakoutRoom] = {
rooms.get(id)
}
def getRooms(): Array[BreakoutRoom] = {
rooms.values.toArray
}
def getAssignedUsers(breakoutId: String): Option[Vector[String]] = {
for {
room <- rooms.get(breakoutId)
} yield room.assignedUsers
}
def updateBreakoutUsers(breakoutId: String, users: Vector[BreakoutUser]): Option[BreakoutRoom] = {
for {
room <- rooms.get(breakoutId)
newroom = room.copy(users = users)
room2 = add(newroom)
} yield room2
}
}

View File

@ -0,0 +1,58 @@
package org.bigbluebutton.core.apps
import org.bigbluebutton.core.api._
import scala.collection.mutable.ArrayBuffer
import org.bigbluebutton.core.MeetingActor
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.LiveMeeting
trait CaptionApp {
this: LiveMeeting =>
val outGW: OutMessageGateway
def handleSendCaptionHistoryRequest(msg: SendCaptionHistoryRequest) {
var history = captionModel.getHistory()
outGW.send(new SendCaptionHistoryReply(mProps.meetingID, mProps.recorded, msg.requesterID, history))
}
def handleUpdateCaptionOwnerRequest(msg: UpdateCaptionOwnerRequest) {
// clear owner from previous locale
if (msg.ownerID.length > 0) {
captionModel.findLocaleByOwnerId(msg.ownerID).foreach(t => {
captionModel.changeTranscriptOwner(t, "")
// send notification that owner has changed
outGW.send(new UpdateCaptionOwnerReply(mProps.meetingID, mProps.recorded, t, ""))
})
}
// create the locale if it doesn't exist
if (captionModel.transcripts contains msg.locale) {
captionModel.changeTranscriptOwner(msg.locale, msg.ownerID)
} else { // change the owner if it does exist
captionModel.newTranscript(msg.locale, msg.ownerID)
}
outGW.send(new UpdateCaptionOwnerReply(mProps.meetingID, mProps.recorded, msg.locale, msg.ownerID))
}
def handleEditCaptionHistoryRequest(msg: EditCaptionHistoryRequest) {
captionModel.findLocaleByOwnerId(msg.userID).foreach(t => {
if (t == msg.locale) {
captionModel.editHistory(msg.startIndex, msg.endIndex, msg.locale, msg.text)
outGW.send(new EditCaptionHistoryReply(mProps.meetingID, mProps.recorded, msg.userID, msg.startIndex, msg.endIndex, msg.locale, msg.text))
}
})
}
def checkCaptionOwnerLogOut(userId: String) {
captionModel.findLocaleByOwnerId(userId).foreach(t => {
captionModel.changeTranscriptOwner(t, "")
// send notification that owner has changed
outGW.send(new UpdateCaptionOwnerReply(mProps.meetingID, mProps.recorded, t, ""))
})
}
}

View File

@ -0,0 +1,53 @@
package org.bigbluebutton.core.apps
import scala.collection.mutable.ArrayBuffer
import scala.collection.immutable.HashMap
class CaptionModel {
var transcripts = Map[String, Array[String]]()
def newTranscript(locale: String, ownerId: String) {
transcripts += locale -> Array(ownerId, "")
}
def findLocaleByOwnerId(userId: String): Option[String] = {
transcripts.find(_._2(0) == userId).foreach(t => {
return Some(t._1)
})
return None
}
def changeTranscriptOwner(locale: String, ownerId: String) {
if (transcripts contains locale) {
transcripts(locale)(0) = ownerId
}
}
def getHistory(): Map[String, Array[String]] = {
var history = Map[String, Array[String]]()
transcripts.foreach(t => {
history += t._1 -> Array(t._2(0), t._2(1))
})
history
}
def editHistory(startIndex: Integer, endIndex: Integer, locale: String, text: String) {
println("editHistory entered")
if (transcripts contains locale) {
println("editHistory found locale:" + locale)
var oText: String = transcripts(locale)(1)
if (startIndex >= 0 && endIndex <= oText.length && startIndex <= endIndex) {
println("editHistory passed index test")
var sText: String = oText.substring(0, startIndex)
var eText: String = oText.substring(endIndex)
transcripts(locale)(1) = (sText + text + eText)
println("editHistory new history is: " + transcripts(locale)(1))
}
}
}
}

View File

@ -2,11 +2,11 @@ package org.bigbluebutton.core.apps
import org.bigbluebutton.core.api._
import scala.collection.mutable.ArrayBuffer
import org.bigbluebutton.core.MeetingActor
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.LiveMeeting
trait ChatApp {
this: MeetingActor =>
this: LiveMeeting =>
val outGW: OutMessageGateway

View File

@ -1,12 +1,12 @@
package org.bigbluebutton.core.apps
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.MeetingActor
import scala.collection.mutable.ArrayBuffer
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.LiveMeeting
trait LayoutApp {
this: MeetingActor =>
this: LiveMeeting =>
val outGW: OutMessageGateway

View File

@ -1,7 +1,6 @@
package org.bigbluebutton.core.apps
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.MeetingActor
import scala.collection.mutable.HashMap
import scala.collection.mutable.ArrayBuffer
import org.bigbluebutton.common.messages.WhiteboardKeyUtil
@ -9,9 +8,10 @@ import org.bigbluebutton.common.messages.WhiteboardKeyUtil
import com.google.gson.Gson
import java.util.ArrayList
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.LiveMeeting
trait PollApp {
this: MeetingActor =>
this: LiveMeeting =>
val outGW: OutMessageGateway
@ -46,8 +46,7 @@ trait PollApp {
handleRespondToPoll(poll, msg)
}
case None => {
val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
sender ! new RespondToPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, msg.pollId)
}
}
}
@ -59,8 +58,7 @@ trait PollApp {
outGW.send(new PollHideResultMessage(mProps.meetingID, mProps.recorded, msg.requesterId, msg.pollId))
}
case None => {
val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
sender ! new HidePollResultReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, msg.pollId)
}
}
}
@ -107,14 +105,13 @@ trait PollApp {
for {
page <- presModel.getCurrentPage()
annotation = new AnnotationVO(poll.id, WhiteboardKeyUtil.DRAW_END_STATUS, WhiteboardKeyUtil.POLL_RESULT_TYPE, shape, page.id)
} this.context.self ! new SendWhiteboardAnnotationRequest(mProps.meetingID, msg.requesterId, annotation)
} handleSendWhiteboardAnnotationRequest(new SendWhiteboardAnnotationRequest(mProps.meetingID, msg.requesterId, annotation))
outGW.send(new PollShowResultMessage(mProps.meetingID, mProps.recorded, msg.requesterId, msg.pollId, poll))
}
case None => {
val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
sender ! new ShowPollResultReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, msg.pollId)
}
}
}
@ -131,8 +128,7 @@ trait PollApp {
outGW.send(new PollStoppedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, poll.id))
}
case None => {
val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
sender ! new StopPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId)
}
}
}
@ -152,8 +148,7 @@ trait PollApp {
outGW.send(new PollStartedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, pollId, poll))
}
case None => {
val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
sender ! new StartPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, pollId)
}
}
}
@ -174,8 +169,7 @@ trait PollApp {
outGW.send(new PollStartedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, pollId, poll))
}
case None => {
val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
sender ! new StartPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, pollId)
}
}
}
@ -204,8 +198,7 @@ trait PollApp {
}
case None => {
val result = new RequestResult(StatusCodes.FORBIDDEN, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
sender ! new RespondToPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, msg.pollId)
}
}
}

View File

@ -1,12 +1,12 @@
package org.bigbluebutton.core.apps
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.MeetingActor
import com.google.gson.Gson
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.LiveMeeting
trait PresentationApp {
this: MeetingActor =>
this: LiveMeeting =>
val outGW: OutMessageGateway
@ -75,8 +75,6 @@ trait PresentationApp {
}
def handleGetPresentationInfo(msg: GetPresentationInfo) {
// println("PresentationApp : handleGetPresentationInfo GetPresentationInfo for meeting [" + msg.meetingID + "] [" + msg.requesterID + "]" )
val curPresenter = usersModel.getCurrentPresenterInfo();
val presenter = new CurrentPresenter(curPresenter.presenterID, curPresenter.presenterName, curPresenter.assignedBy)
val presentations = presModel.getPresentations
@ -108,7 +106,7 @@ trait PresentationApp {
// printPresentations
usersModel.getCurrentPresenter() foreach { pres =>
this.context.self ! StopPollRequest(mProps.meetingID, pres.userID)
handleStopPollRequest(StopPollRequest(mProps.meetingID, pres.userID))
}
}

View File

@ -3,13 +3,13 @@ package org.bigbluebutton.core.apps
import org.bigbluebutton.core.api._
import scala.collection.mutable.HashMap
import java.util.ArrayList
import org.bigbluebutton.core.MeetingActor
import scala.collection.mutable.ArrayBuffer
import scala.collection.immutable.ListSet
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.LiveMeeting
trait UsersApp {
this: MeetingActor =>
this: LiveMeeting =>
val outGW: OutMessageGateway
@ -111,7 +111,7 @@ trait UsersApp {
* Sometimes, the actor seems to hang and doesn't anymore accept messages. This is a simple
* audit to check whether the actor is still alive. (ralam feb 25, 2015)
*/
sender ! new ValidateAuthTokenReply(mProps.meetingID, msg.userId, msg.token, false, msg.correlationId)
//sender ! new ValidateAuthTokenReply(mProps.meetingID, msg.userId, msg.token, false, msg.correlationId)
}
def handleRegisterUser(msg: RegisterUser) {
@ -415,6 +415,8 @@ trait UsersApp {
vu.userId, u.userID, u.externUserID, vu.callerName,
vu.callerNum, vu.muted, vu.talking, vu.avatarURL, u.listenOnly));
}
checkCaptionOwnerLogOut(u.userID)
}
startCheckingIfWeNeedToEndVoiceConf()
@ -609,7 +611,7 @@ trait UsersApp {
def assignNewPresenter(newPresenterID: String, newPresenterName: String, assignedBy: String) {
// Stop poll if one is running as presenter left.
this.context.self ! StopPollRequest(mProps.meetingID, assignedBy)
handleStopPollRequest(StopPollRequest(mProps.meetingID, assignedBy))
if (usersModel.hasUser(newPresenterID)) {

View File

@ -1,14 +1,14 @@
package org.bigbluebutton.core.apps
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.MeetingActor
import org.bigbluebutton.common.messages.WhiteboardKeyUtil
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.LiveMeeting
case class Whiteboard(id: String, shapes: Seq[AnnotationVO])
trait WhiteboardApp {
this: MeetingActor =>
this: LiveMeeting =>
val outGW: OutMessageGateway

View File

@ -0,0 +1,34 @@
package org.bigbluebutton.core.bus
import akka.actor.ActorRef
import akka.event.EventBus
import akka.event.ActorEventBus
import akka.event.LookupClassification
import org.bigbluebutton.core.api.InMessage
case class BigBlueButtonEvent(val topic: String, val payload: InMessage)
class IncomingEventBus extends EventBus with LookupClassification {
type Event = BigBlueButtonEvent
type Classifier = String
type Subscriber = ActorRef
// is used for extracting the classifier from the incoming events
override protected def classify(event: Event): Classifier = event.topic
// will be invoked for each event for all subscribers which registered themselves
// for the events classifier
override protected def publish(event: Event, subscriber: Subscriber): Unit = {
subscriber ! event.payload
}
// must define a full order over the subscribers, expressed as expected from
// `java.lang.Comparable.compare`
override protected def compareSubscribers(a: Subscriber, b: Subscriber): Int =
a.compareTo(b)
// determines the initial size of the index data structure
// used internally (i.e. the expected number of different classifiers)
override protected def mapSize: Int = 128
}

View File

@ -0,0 +1,34 @@
package org.bigbluebutton.core.bus
import akka.actor.ActorRef
import akka.event.EventBus
import akka.event.ActorEventBus
import akka.event.LookupClassification
import org.bigbluebutton.core.api._
case class BigBlueButtonOutMessage(val topic: String, val payload: IOutMessage)
class OutgoingEventBus extends EventBus with LookupClassification {
type Event = BigBlueButtonOutMessage
type Classifier = String
type Subscriber = ActorRef
// is used for extracting the classifier from the incoming events
override protected def classify(event: Event): Classifier = event.topic
// will be invoked for each event for all subscribers which registered themselves
// for the events classifier
override protected def publish(event: Event, subscriber: Subscriber): Unit = {
subscriber ! event.payload
}
// must define a full order over the subscribers, expressed as expected from
// `java.lang.Comparable.compare`
override protected def compareSubscribers(a: Subscriber, b: Subscriber): Int =
a.compareTo(b)
// determines the initial size of the index data structure
// used internally (i.e. the expected number of different classifiers)
override protected def mapSize: Int = 128
}

View File

@ -0,0 +1,44 @@
package org.bigbluebutton.core.pubsub.senders
import org.bigbluebutton.common.messages.Constants
import scala.collection.mutable.HashMap
import org.bigbluebutton.core.api._
import collection.JavaConverters._
import scala.collection.JavaConversions._
import org.bigbluebutton.core.messaging.Util
object CaptionMessageToJsonConverter {
def sendCaptionHistoryReplyToJson(msg: SendCaptionHistoryReply): String = {
val payload = new java.util.HashMap[String, Any]()
payload.put(Constants.MEETING_ID, msg.meetingID)
payload.put(Constants.REQUESTER_ID, msg.requesterID)
payload.put(Constants.CAPTION_HISTORY, mapAsJavaMap(msg.history))
val header = Util.buildHeader(MessageNames.SEND_CAPTION_HISTORY_REPLY, Some(msg.requesterID))
Util.buildJson(header, payload)
}
def updateCaptionOwnerReplyToJson(msg: UpdateCaptionOwnerReply): String = {
val payload = new java.util.HashMap[String, Any]()
payload.put(Constants.MEETING_ID, msg.meetingID)
payload.put(Constants.LOCALE, msg.locale)
payload.put(Constants.OWNER_ID, msg.ownerID)
val header = Util.buildHeader(MessageNames.UPDATE_CAPTION_OWNER, None)
Util.buildJson(header, payload)
}
def editCaptionHistoryReplyToJson(msg: EditCaptionHistoryReply): String = {
val payload = new java.util.HashMap[String, Any]()
payload.put(Constants.MEETING_ID, msg.meetingID)
payload.put(Constants.USER_ID, msg.userID)
payload.put(Constants.START_INDEX, msg.startIndex)
payload.put(Constants.END_INDEX, msg.endIndex)
payload.put(Constants.LOCALE, msg.locale)
payload.put(Constants.TEXT, msg.text)
val header = Util.buildHeader(MessageNames.EDIT_CAPTION_HISTORY, None)
Util.buildJson(header, payload)
}
}

View File

@ -1,9 +1,12 @@
package org.bigbluebutton.core.pubsub.senders
import org.bigbluebutton.core.messaging.Util
import org.bigbluebutton.core.api._
import com.google.gson.Gson
import scala.collection.JavaConverters._
import org.bigbluebutton.core.messaging.Util
import org.bigbluebutton.messages._
import spray.json.JsArray
import spray.json.JsObject
import spray.json.JsString
object MeetingMessageToJsonConverter {
def meetingDestroyedToJson(msg: MeetingDestroyed): String = {
@ -129,4 +132,74 @@ object MeetingMessageToJsonConverter {
val header = Util.buildHeader(MessageNames.GET_ALL_MEETINGS_REPLY, None)
Util.buildJson(header, payload)
}
}
def breakoutRoomsListOutMessageToJson(msg: BreakoutRoomsListOutMessage): String = {
val payload = new java.util.HashMap[String, Any]()
payload.put("meetingId", msg.meetingId)
payload.put("rooms", msg.rooms.toArray)
val header = Util.buildHeader(BreakoutRoomsList.NAME, None)
Util.buildJson(header, payload)
}
def breakoutRoomStartedOutMessageToJson(msg: BreakoutRoomStartedOutMessage): String = {
val payload = new java.util.HashMap[String, Any]()
payload.put("meetingId", msg.meetingId)
payload.put("breakoutId", msg.breakout.breakoutId)
payload.put("name", msg.breakout.name)
val header = Util.buildHeader(BreakoutRoomStarted.NAME, None)
Util.buildJson(header, payload)
}
def breakoutRoomEndedOutMessageToJson(msg: BreakoutRoomEndedOutMessage): String = {
val payload = new java.util.HashMap[String, Any]()
payload.put("meetingId", msg.meetingId)
payload.put("breakoutId", msg.breakoutId)
val header = Util.buildHeader(BreakoutRoomClosed.NAME, None)
Util.buildJson(header, payload)
}
def breakoutRoomJoinURLOutMessageToJson(msg: BreakoutRoomJoinURLOutMessage): String = {
val payload = new java.util.HashMap[String, Any]()
payload.put("meetingId", msg.meetingId)
payload.put("breakoutId", msg.breakoutId)
payload.put("userId", msg.userId)
payload.put("joinURL", msg.joinURL)
val header = Util.buildHeader(BreakoutRoomJoinURL.NAME, None)
Util.buildJson(header, payload)
}
def updateBreakoutUsersOutMessageToJson(msg: UpdateBreakoutUsersOutMessage): String = {
val payload = new java.util.HashMap[String, Any]()
payload.put("meetingId", msg.meetingId)
payload.put("breakoutId", msg.breakoutId)
payload.put("recorded", msg.recorded)
payload.put("users", msg.users.toArray)
val header = Util.buildHeader(UpdateBreakoutUsers.NAME, None)
Util.buildJson(header, payload)
}
def meetingTimeRemainingUpdateToJson(msg: MeetingTimeRemainingUpdate): String = {
val payload = new java.util.HashMap[String, Any]()
payload.put("meetingId", msg.meetingId)
payload.put("recorded", msg.recorded)
payload.put("timeRemaining", msg.timeRemaining)
val header = Util.buildHeader(TimeRemainingUpdate.NAME, None)
Util.buildJson(header, payload)
}
def breakoutRoomsTimeRemainingUpdateToJson(msg: BreakoutRoomsTimeRemainingUpdateOutMessage): String = {
val payload = new java.util.HashMap[String, Any]()
payload.put("meetingId", msg.meetingId)
payload.put("recorded", msg.recorded)
payload.put("timeRemaining", msg.timeRemaining)
val header = Util.buildHeader(BreakoutRoomsTimeRemainingUpdate.NAME, None)
Util.buildJson(header, payload)
}
}

View File

@ -0,0 +1,6 @@
package org.bigbluebutton.xml;
public class Simple {
public int x = 1;
public int y = 2;
}

View File

@ -0,0 +1,27 @@
package org.bigbluebutton.core
trait AppsTestFixtures {
val meetingId = "testMeetingId"
val externalMeetingId = "testExternalMeetingId"
val meetingName = "test meeting"
val record = false
val voiceConfId = "85115"
val durationInMinutes = 10
val autoStartRecording = false
val allowStartStopRecording = false
val moderatorPassword = "modpass"
val viewerPassword = "viewpass"
val createTime = System.currentTimeMillis
val createDate = "Oct 26, 2015"
val isBreakout = false
val mProps = new MeetingProperties(meetingId, externalMeetingId,
meetingName, record,
voiceConfId,
durationInMinutes,
autoStartRecording, allowStartStopRecording,
moderatorPassword, viewerPassword,
createTime, createDate, isBreakout)
}

View File

@ -0,0 +1,5 @@
package org.bigbluebutton.core
trait BreakoutRoomsTestFixtures extends AppsTestFixtures {
}

View File

@ -0,0 +1,27 @@
package org.bigbluebutton.core
import scala.util.Success
import scala.util.Failure
import org.scalatest._
import org.bigbluebutton.core.api.CreateBreakoutRooms
class JsonMessageDecoderTests extends UnitSpec with JsonMessageFixtures {
it should "return a CreateBreakoutRoomsRequestMessage" in {
JsonMessageDecoder.decode(createBreakoutRoomsRequestMessage) match {
case Some(validMsg) => {
val vm = validMsg.asInstanceOf[CreateBreakoutRooms]
assert(vm.durationInMinutes == 20)
}
case None => fail("Failed to decode message")
}
}
it should "fail to decode CreateBreakoutRoomsRequestMessage" in {
JsonMessageDecoder.decode(invalidCreateBreakoutRoomsRequestMessage) match {
case Some(validMsg) => fail("Should have failed to decode message")
case None => assert(true)
}
}
}

View File

@ -0,0 +1,11 @@
package org.bigbluebutton.core
trait JsonMessageFixtures {
val createBreakoutRoomsRequestMessage = """
{"header":{"name":"CreateBreakoutRoomsRequest"},"payload":{"meetingId":"abc123","rooms":[{"name":"room1","users":["Tidora","Nidora","Tinidora"]},{"name":"room2","users":["Jose","Wally","Paolo"]},{"name":"room3","users":["Alden","Yaya Dub"]}],"durationInMinutes":20}}
"""
val invalidCreateBreakoutRoomsRequestMessage = """
{"header":{"name":"CreateBreakoutRoomsRequest"},"payload":{"meetingId2":"abc123","rooms":[{"name":"room1","users":["Tidora","Nidora","Tinidora"]},{"name":"room2","users":["Jose","Wally","Paolo"]},{"name":"room3","users":["Alden","Yaya Dub"]}],"durationInMinutes":20}}
"""
}

View File

@ -0,0 +1,5 @@
package org.bigbluebutton.core
trait MeetingManagerTestFixtures extends AppsTestFixtures {
}

View File

@ -0,0 +1,153 @@
package org.bigbluebutton.core
import scala.util.Random
import com.typesafe.config.ConfigFactory
import akka.actor.Actor
import akka.actor.ActorRef
import akka.actor.ActorSystem
import akka.actor.Props
import akka.testkit.DefaultTimeout
import akka.testkit.ImplicitSender
import akka.testkit.TestKit
import scala.concurrent.duration._
import scala.collection.immutable
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.WordSpecLike
import org.scalatest.BeforeAndAfterAll
import org.scalatest.Matchers
/**
* a Test to show some TestKit examples
*/
class TestKitUsageSpec extends TestKit(ActorSystem("TestKitUsageSpec",
ConfigFactory.parseString(TestKitUsageSpec.config)))
with DefaultTimeout with ImplicitSender with WordSpecLike
with Matchers with BeforeAndAfterAll {
import TestKitUsageSpec._
val echoRef = system.actorOf(Props[EchoActor])
val forwardRef = system.actorOf(Props(classOf[ForwardingActor], testActor))
val filterRef = system.actorOf(Props(classOf[FilteringActor], testActor))
val randomHead = Random.nextInt(6)
val randomTail = Random.nextInt(10)
val headList = immutable.Seq().padTo(randomHead, "0")
val tailList = immutable.Seq().padTo(randomTail, "1")
val seqRef =
system.actorOf(Props(classOf[SequencingActor], testActor, headList, tailList))
override def afterAll {
shutdown(system)
}
"An EchoActor" should {
"Respond with the same message it receives" in {
within(500 millis) {
echoRef ! "test"
expectMsg("test")
}
}
}
"A ForwardingActor" should {
"Forward a message it receives" in {
within(500 millis) {
forwardRef ! "test"
expectMsg("test")
}
}
}
"A FilteringActor" should {
"Filter all messages, except expected messagetypes it receives" in {
var messages = Seq[String]()
within(500 millis) {
filterRef ! "test"
expectMsg("test")
filterRef ! 1
expectNoMsg
filterRef ! "some"
filterRef ! "more"
filterRef ! 1
filterRef ! "text"
filterRef ! 1
receiveWhile(500 millis) {
case msg: String => messages = msg +: messages
}
}
messages.length should be(3)
messages.reverse should be(Seq("some", "more", "text"))
}
}
"A SequencingActor" should {
"receive an interesting message at some point " in {
within(500 millis) {
ignoreMsg {
case msg: String => msg != "something"
}
seqRef ! "something"
expectMsg("something")
ignoreMsg {
case msg: String => msg == "1"
}
expectNoMsg
ignoreNoMsg
}
}
}
}
object TestKitUsageSpec {
// Define your test specific configuration here
val config = """
akka {
loglevel = "WARNING"
}
"""
/**
* An Actor that echoes everything you send to it
*/
class EchoActor extends Actor {
def receive = {
case msg => sender ! msg
}
}
/**
* An Actor that forwards every message to a next Actor
*/
class ForwardingActor(next: ActorRef) extends Actor {
def receive = {
case msg => next ! msg
}
}
/**
* An Actor that only forwards certain messages to a next Actor
*/
class FilteringActor(next: ActorRef) extends Actor {
def receive = {
case msg: String => next ! msg
case _ => None
}
}
/**
* An actor that sends a sequence of messages with a random head list, an
* interesting value and a random tail list. The idea is that you would
* like to test that the interesting value is received and that you cant
* be bothered with the rest
*/
class SequencingActor(next: ActorRef, head: immutable.Seq[String],
tail: immutable.Seq[String]) extends Actor {
def receive = {
case msg => {
head foreach { next ! _ }
next ! msg
tail foreach { next ! _ }
}
}
}
}

View File

@ -0,0 +1,8 @@
package org.bigbluebutton.core
import org.scalatest.FlatSpec
import org.scalatest.BeforeAndAfterAll
import org.scalatest.WordSpec
import org.scalatest.Matchers
class UnitSpec extends FlatSpec with Matchers with BeforeAndAfterAll

View File

@ -0,0 +1,23 @@
package org.bigbluebutton.core.apps
import collection.mutable.Stack
import org.scalatest._
import org.bigbluebutton.core.UnitSpec
class BreakoutRoomModelSpec extends UnitSpec {
"A Stack" should "pop values in last-in-first-out order" in {
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
assert(stack.pop() === 2)
assert(stack.pop() === 1)
}
it should "throw NoSuchElementException if an empty stack is popped" in {
val emptyStack = new Stack[String]
intercept[NoSuchElementException] {
emptyStack.pop()
}
}
}

View File

@ -0,0 +1,74 @@
package org.bigbluebutton.core.apps
import collection.mutable.Stack
import org.scalatest._
import org.bigbluebutton.core.UnitSpec
class BreakoutRoomsUtilSpec extends UnitSpec {
it should "return a pdfURL" in {
val baseURL = "http://localhost/pre1/page1."
val swfURL = baseURL + "swf"
val pdfURL = BreakoutRoomsUtil.fromSWFtoPDF(swfURL)
assert(pdfURL == baseURL + "pdf")
}
it should "return a meetingId" in {
val mainMeetingId = "abc-123"
val index = 1
val result = mainMeetingId.concat("-").concat(index.toString())
val breakoutMeetingId = BreakoutRoomsUtil.createMeetingId(mainMeetingId, index)
assert(breakoutMeetingId == result)
}
it should "return a voiceConfId" in {
val voiceConfId = "85115"
val index = 1
val result = voiceConfId.concat(index.toString())
val breakoutMeetingId = BreakoutRoomsUtil.createVoiceConfId(voiceConfId, index)
assert(breakoutMeetingId == result)
}
it should "encode the strings properly" in {
val username = "User 4621018"
val encodedUsername = "User+4621018"
val user = BreakoutRoomsUtil.urlEncode(username)
assert(user == encodedUsername)
}
it should "create a base string" in {
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&password=mp&redirect=true"
val params = new collection.mutable.HashMap[String, String]
params += "fullName" -> BreakoutRoomsUtil.urlEncode("User 4621018")
params += "isBreakout" -> BreakoutRoomsUtil.urlEncode("true")
params += "meetingID" -> BreakoutRoomsUtil.urlEncode("random-1853792")
params += "password" -> BreakoutRoomsUtil.urlEncode("mp")
params += "redirect" -> BreakoutRoomsUtil.urlEncode("true")
val result = BreakoutRoomsUtil.createBaseString(params)
assert(result == baseString)
}
it should "calculate the checksum of join url" in {
val sharedSecret = "a820d30da2db356124fce5bd5d8054b4"
val checksum = "6baef866df491ae82df992eb14f7f8511d5b77f3"
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&password=mp&redirect=true"
val joinChecksum = BreakoutRoomsUtil.calculateChecksum("join", baseString, sharedSecret)
assert(joinChecksum == checksum)
}
it should "create a join api url" in {
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&password=mp&redirect=true"
val webAPI = "http://www.example.com/bigbluebutton/api/"
val joinAPI = "join"
val checksum = "6baef866df491ae82df992eb14f7f8511d5b77f3"
val joinURL = webAPI.concat(joinAPI).concat("?").concat(baseString).concat("&checksum=").concat(checksum)
val result = BreakoutRoomsUtil.createJoinURL(webAPI, joinAPI, baseString, checksum);
assert(joinURL == result)
}
}

View File

@ -39,4 +39,4 @@
# - setup with a configuration tool after unzip
# - use the path to the application.ini file
# -Dconfig.file=${{path_to}}/conf/application.conf
-Dconfig.file=/usr/share/bbb-apps-akka/conf/application.conf

View File

@ -52,7 +52,7 @@ libraryDependencies ++= {
"com.google.code.gson" % "gson" % "1.7.1",
"redis.clients" % "jedis" % "2.1.0",
"org.apache.commons" % "commons-lang3" % "3.2",
"org.bigbluebutton" % "bbb-common-message" % "0.0.17",
"org.bigbluebutton" % "bbb-common-message" % "0.0.18-SNAPSHOT",
"org.bigbluebutton" % "bbb-fsesl-client" % "0.0.4"
)}

View File

@ -1,12 +1,12 @@
package org.bigbluebutton.freeswitch.pubsub.receivers;
import org.bigbluebutton.common.messages.EjectAllUsersFromVoiceConfRequestMessage;
import org.bigbluebutton.common.messages.EjectUserFromVoiceConfRequestMessage;
import org.bigbluebutton.common.messages.GetUsersFromVoiceConfRequestMessage;
import org.bigbluebutton.common.messages.MuteUserInVoiceConfRequestMessage;
import org.bigbluebutton.common.messages.StartRecordingVoiceConfRequestMessage;
import org.bigbluebutton.common.messages.StopRecordingVoiceConfRequestMessage;
import org.bigbluebutton.common.messages.TransferUserToVoiceConfRequestMessage;
import org.bigbluebutton.common.messages.DeskShareStartRTMPBroadcastEventMessage;
import org.bigbluebutton.common.messages.DeskShareStopRTMPBroadcastEventMessage;
import org.bigbluebutton.common.messages.DeskShareHangUpEventMessage;
@ -17,16 +17,18 @@ import com.google.gson.JsonParser;
public class RedisMessageReceiver {
public static final String TO_VOICE_CONF_CHANNEL = "bigbluebutton:to-voice-conf";
public static final String TO_VOICE_CONF_PATTERN = TO_VOICE_CONF_CHANNEL + ":*";
public static final String TO_VOICE_CONF_SYSTEM_CHAN = TO_VOICE_CONF_CHANNEL + ":system";
public static final String TO_VOICE_CONF_CHANNEL = "bigbluebutton:to-voice-conf";
public static final String TO_VOICE_CONF_PATTERN = TO_VOICE_CONF_CHANNEL
+ ":*";
public static final String TO_VOICE_CONF_SYSTEM_CHAN = TO_VOICE_CONF_CHANNEL
+ ":system";
private final FreeswitchApplication fsApp;
public RedisMessageReceiver(FreeswitchApplication fsApp) {
this.fsApp = fsApp;
}
public void handleMessage(String pattern, String channel, String message) {
if (channel.equalsIgnoreCase(TO_VOICE_CONF_SYSTEM_CHAN)) {
JsonParser parser = new JsonParser();
@ -38,24 +40,27 @@ public class RedisMessageReceiver {
if (header.has("name")) {
String messageName = header.get("name").getAsString();
switch (messageName) {
case EjectAllUsersFromVoiceConfRequestMessage.EJECT_ALL_VOICE_USERS_REQUEST:
processEjectAllVoiceUsersRequestMessage(message);
break;
case EjectUserFromVoiceConfRequestMessage.EJECT_VOICE_USER_REQUEST:
processEjectVoiceUserRequestMessage(message);
break;
case GetUsersFromVoiceConfRequestMessage.GET_VOICE_USERS_REQUEST:
processGetVoiceUsersRequestMessage(message);
break;
case MuteUserInVoiceConfRequestMessage.MUTE_VOICE_USER_REQUEST:
processMuteVoiceUserRequestMessage(message);
break;
case StartRecordingVoiceConfRequestMessage.START_RECORD_VOICE_CONF_REQUEST:
processStartRecordingVoiceConfRequestMessage(message);
break;
case StopRecordingVoiceConfRequestMessage.STOP_RECORD_VOICE_CONF_REQUEST:
processStopRecordingVoiceConfRequestMessage(message);
break;
case EjectAllUsersFromVoiceConfRequestMessage.EJECT_ALL_VOICE_USERS_REQUEST:
processEjectAllVoiceUsersRequestMessage(message);
break;
case EjectUserFromVoiceConfRequestMessage.EJECT_VOICE_USER_REQUEST:
processEjectVoiceUserRequestMessage(message);
break;
case GetUsersFromVoiceConfRequestMessage.GET_VOICE_USERS_REQUEST:
processGetVoiceUsersRequestMessage(message);
break;
case MuteUserInVoiceConfRequestMessage.MUTE_VOICE_USER_REQUEST:
processMuteVoiceUserRequestMessage(message);
break;
case TransferUserToVoiceConfRequestMessage.TRANSFER_USER_TO_VOICE_CONF_REQUEST:
processTransferUserToVoiceConfRequestMessage(message);
break;
case StartRecordingVoiceConfRequestMessage.START_RECORD_VOICE_CONF_REQUEST:
processStartRecordingVoiceConfRequestMessage(message);
break;
case StopRecordingVoiceConfRequestMessage.STOP_RECORD_VOICE_CONF_REQUEST:
processStopRecordingVoiceConfRequestMessage(message);
break;
case DeskShareStartRTMPBroadcastEventMessage.DESKSHARE_START_RTMP_BROADCAST_MESSAGE:
System.out.println("RedisMessageReceiver got DESKSHARE_START_RTMP_BROADCAST_MESSAGE");
processDeskShareStartRTMPBroadcastEventMessage(message);
@ -90,32 +95,45 @@ public class RedisMessageReceiver {
}
private void processEjectAllVoiceUsersRequestMessage(String json) {
EjectAllUsersFromVoiceConfRequestMessage msg = EjectAllUsersFromVoiceConfRequestMessage.fromJson(json);
EjectAllUsersFromVoiceConfRequestMessage msg = EjectAllUsersFromVoiceConfRequestMessage
.fromJson(json);
fsApp.ejectAll(msg.voiceConfId);
}
private void processEjectVoiceUserRequestMessage(String json) {
EjectUserFromVoiceConfRequestMessage msg = EjectUserFromVoiceConfRequestMessage.fromJson(json);
EjectUserFromVoiceConfRequestMessage msg = EjectUserFromVoiceConfRequestMessage
.fromJson(json);
fsApp.eject(msg.voiceConfId, msg.voiceUserId);
}
private void processGetVoiceUsersRequestMessage(String json) {
GetUsersFromVoiceConfRequestMessage msg = GetUsersFromVoiceConfRequestMessage.fromJson(json);
GetUsersFromVoiceConfRequestMessage msg = GetUsersFromVoiceConfRequestMessage
.fromJson(json);
fsApp.getAllUsers(msg.voiceConfId);
}
private void processMuteVoiceUserRequestMessage(String json) {
MuteUserInVoiceConfRequestMessage msg = MuteUserInVoiceConfRequestMessage.fromJson(json);
MuteUserInVoiceConfRequestMessage msg = MuteUserInVoiceConfRequestMessage
.fromJson(json);
fsApp.muteUser(msg.voiceConfId, msg.voiceUserId, msg.mute);
}
private void processTransferUserToVoiceConfRequestMessage(String json) {
TransferUserToVoiceConfRequestMessage msg = TransferUserToVoiceConfRequestMessage
.fromJson(json);
fsApp.transferUserToMeeting(msg.voiceConfId, msg.targetVoiceConfId,
msg.voiceUserId);
}
private void processStartRecordingVoiceConfRequestMessage(String json) {
StartRecordingVoiceConfRequestMessage msg = StartRecordingVoiceConfRequestMessage.fromJson(json);
StartRecordingVoiceConfRequestMessage msg = StartRecordingVoiceConfRequestMessage
.fromJson(json);
fsApp.startRecording(msg.voiceConfId, msg.meetingId);
}
private void processStopRecordingVoiceConfRequestMessage(String json) {
StopRecordingVoiceConfRequestMessage msg = StopRecordingVoiceConfRequestMessage.fromJson(json);
StopRecordingVoiceConfRequestMessage msg = StopRecordingVoiceConfRequestMessage
.fromJson(json);
fsApp.stopRecording(msg.voiceConfId, msg.meetingId, msg.recordStream);
}
}

View File

@ -1,142 +1,160 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.freeswitch.voice.freeswitch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.bigbluebutton.freeswitch.voice.events.ConferenceEventListener;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.BroadcastConferenceCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectAllUsersCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectUserCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.GetAllUsersCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.MuteUserCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.RecordConferenceCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.TransferUsetToMeetingCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.*;
import org.freeswitch.esl.client.inbound.Client;
import org.freeswitch.esl.client.inbound.InboundConnectionFailure;
import org.freeswitch.esl.client.manager.ManagerConnection;
import org.freeswitch.esl.client.transport.message.EslMessage;
public class ConnectionManager {
public class ConnectionManager {
private static final String EVENT_NAME = "Event-Name";
private static final ScheduledExecutorService connExec = Executors.newSingleThreadScheduledExecutor();
private final ManagerConnection manager;
private ScheduledFuture<ConnectThread> connectTask;
private volatile boolean subscribed = false;
private final ConferenceEventListener conferenceEventListener;
private final ESLEventListener eslEventListener;
public ConnectionManager(ManagerConnection connManager, ESLEventListener eventListener, ConferenceEventListener confListener) {
this.manager = connManager;
this.eslEventListener = eventListener;
this.conferenceEventListener = confListener;
}
private void connect() {
try {
Client c = manager.getESLClient();
if (! c.canSend()) {
private static final String EVENT_NAME = "Event-Name";
private static final ScheduledExecutorService connExec = Executors
.newSingleThreadScheduledExecutor();
private final ManagerConnection manager;
private ScheduledFuture<ConnectThread> connectTask;
private volatile boolean subscribed = false;
private final ConferenceEventListener conferenceEventListener;
private final ESLEventListener eslEventListener;
public ConnectionManager(ManagerConnection connManager,
ESLEventListener eventListener, ConferenceEventListener confListener) {
this.manager = connManager;
this.eslEventListener = eventListener;
this.conferenceEventListener = confListener;
}
private void connect() {
try {
Client c = manager.getESLClient();
if (!c.canSend()) {
System.out.println("Attempting to connect to FreeSWITCH ESL");
subscribed = false;
manager.connect();
} else {
if (!subscribed) {
System.out.println("Subscribing for ESL events.");
c.cancelEventSubscriptions();
c.addEventListener(eslEventListener);
c.setEventSubscriptions( "plain", "all" );
c.addEventFilter( EVENT_NAME, "heartbeat" );
c.addEventFilter( EVENT_NAME, "custom" );
c.addEventFilter( EVENT_NAME, "background_job" );
subscribed = true;
}
}
subscribed = false;
manager.connect();
} else {
if (!subscribed) {
System.out.println("Subscribing for ESL events.");
c.cancelEventSubscriptions();
c.addEventListener(eslEventListener);
c.setEventSubscriptions("plain", "all");
c.addEventFilter(EVENT_NAME, "heartbeat");
c.addEventFilter(EVENT_NAME, "custom");
c.addEventFilter(EVENT_NAME, "background_job");
subscribed = true;
}
}
} catch (InboundConnectionFailure e) {
System.out.println("Failed to connect to ESL");
}
}
}
public void start() {
System.out.println("Starting FreeSWITCH ESL connection manager.");
ConnectThread connector = new ConnectThread();
connectTask = (ScheduledFuture<ConnectThread>) connExec.scheduleAtFixedRate(connector, 5, 5, TimeUnit.SECONDS);
connectTask = (ScheduledFuture<ConnectThread>) connExec
.scheduleAtFixedRate(connector, 5, 5, TimeUnit.SECONDS);
}
public void stop() {
if (connectTask != null) {
connectTask.cancel(true);
}
}
}
private class ConnectThread implements Runnable {
public void run() {
connect();
}
}
public void broadcast(BroadcastConferenceCommand rcc) {
Client c = manager.getESLClient();
if (c.canSend()) {
EslMessage response = c.sendSyncApiCommand(rcc.getCommand(), rcc.getCommandArgs());
rcc.handleResponse(response, conferenceEventListener);
EslMessage response = c.sendSyncApiCommand(rcc.getCommand(),
rcc.getCommandArgs());
rcc.handleResponse(response, conferenceEventListener);
}
}
public void getUsers(GetAllUsersCommand prc) {
Client c = manager.getESLClient();
if (c.canSend()) {
EslMessage response = c.sendSyncApiCommand(prc.getCommand(), prc.getCommandArgs());
prc.handleResponse(response, conferenceEventListener);
EslMessage response = c.sendSyncApiCommand(prc.getCommand(),
prc.getCommandArgs());
prc.handleResponse(response, conferenceEventListener);
}
}
public void mute(MuteUserCommand mpc) {
System.out.println("Got mute request from FSApplication.");
Client c = manager.getESLClient();
if (c.canSend()) {
System.out.println("Issuing command to FS ESL.");
c.sendAsyncApiCommand( mpc.getCommand(), mpc.getCommandArgs());
c.sendAsyncApiCommand(mpc.getCommand(), mpc.getCommandArgs());
}
}
public void tranfer(TransferUsetToMeetingCommand tutmc) {
Client c = manager.getESLClient();
if (c.canSend()) {
c.sendAsyncApiCommand(tutmc.getCommand(), tutmc.getCommandArgs());
}
}
public void eject(EjectUserCommand mpc) {
Client c = manager.getESLClient();
if (c.canSend()) {
c.sendAsyncApiCommand( mpc.getCommand(), mpc.getCommandArgs());
}
c.sendAsyncApiCommand(mpc.getCommand(), mpc.getCommandArgs());
}
}
public void ejectAll(EjectAllUsersCommand mpc) {
Client c = manager.getESLClient();
if (c.canSend()) {
c.sendAsyncApiCommand( mpc.getCommand(), mpc.getCommandArgs());
c.sendAsyncApiCommand(mpc.getCommand(), mpc.getCommandArgs());
}
}
public void record(RecordConferenceCommand rcc) {
Client c = manager.getESLClient();
if (c.canSend()) {
EslMessage response = c.sendSyncApiCommand(rcc.getCommand(), rcc.getCommandArgs());
rcc.handleResponse(response, conferenceEventListener);
EslMessage response = c.sendSyncApiCommand(rcc.getCommand(),
rcc.getCommandArgs());
rcc.handleResponse(response, conferenceEventListener);
}
}
@ -157,4 +175,4 @@ public class ConnectionManager {
huCmd.handleResponse(response, conferenceEventListener);
}
}
}
}

View File

@ -24,14 +24,23 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.BroadcastConferenceCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectAllUsersCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectUserCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.FreeswitchCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.GetAllUsersCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.MuteUserCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.RecordConferenceCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.TransferUsetToMeetingCommand;
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.*;
public class FreeswitchApplication {
private static final int SENDERTHREADS = 1;
private static final Executor msgSenderExec = Executors.newFixedThreadPool(SENDERTHREADS);
private static final Executor runExec = Executors.newFixedThreadPool(SENDERTHREADS);
private static final Executor msgSenderExec = Executors
.newFixedThreadPool(SENDERTHREADS);
private static final Executor runExec = Executors
.newFixedThreadPool(SENDERTHREADS);
private BlockingQueue<FreeswitchCommand> messages = new LinkedBlockingQueue<FreeswitchCommand>();
private final ConnectionManager manager;
@ -53,6 +62,32 @@ public class FreeswitchApplication {
}
}
public void transferUserToMeeting(String voiceConfId,
String targetVoiceConfId, String voiceUserId) {
TransferUsetToMeetingCommand tutmc = new TransferUsetToMeetingCommand(
voiceConfId, targetVoiceConfId, voiceUserId, USER);
queueMessage(tutmc);
}
public void start() {
sendMessages = true;
Runnable sender = new Runnable() {
public void run() {
while (sendMessages) {
FreeswitchCommand message;
try {
message = messages.take();
sendMessageToFreeswitch(message);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
msgSenderExec.execute(sender);
}
public void getAllUsers(String voiceConfId) {
GetAllUsersCommand prc = new GetAllUsersCommand(voiceConfId, USER);
queueMessage(prc);
@ -134,27 +169,7 @@ public class FreeswitchApplication {
runExec.execute(task);
}
public void start() {
sendMessages = true;
Runnable sender = new Runnable() {
public void run() {
while (sendMessages) {
FreeswitchCommand message;
try {
message = messages.take();
sendMessageToFreeswitch(message);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
msgSenderExec.execute(sender);
public void stop() {
sendMessages = false;
}
public void stop() {
sendMessages = false;
}
}
}

View File

@ -0,0 +1,39 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.freeswitch.voice.freeswitch.actions;
public class TransferUsetToMeetingCommand extends FreeswitchCommand {
private final String targetRoom;
private final String participant;
public TransferUsetToMeetingCommand(String room, String targetRoom,
String participant, String requesterId) {
super(room, requesterId);
this.targetRoom = targetRoom;
this.participant = participant;
}
@Override
public String getCommandArgs() {
return room + SPACE + "transfer" + SPACE + targetRoom + SPACE
+ participant;
}
}

View File

@ -4,8 +4,9 @@ akka {
receive = on
}
}
loglevel = INFO
stdout-loglevel = "INFO"
loggers = ["akka.event.slf4j.Slf4jLogger"]
loglevel = "DEBUG"
stdout-loglevel = "DEBUG"
rediscala-subscriber-worker-dispatcher {
mailbox-type = "akka.dispatch.SingleConsumerOnlyUnboundedMailbox"

View File

@ -22,7 +22,7 @@
<logger name="org.bigbluebutton" level="DEBUG" />
<logger name="org.freeswitch.esl" level="WARN" />
<root level="INFO">
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE" />
</root>

View File

@ -37,4 +37,4 @@ object Boot extends App with SystemConfiguration {
val redisMsgReceiver = new RedisMessageReceiver(fsApplication)
val redisSubscriberActor = system.actorOf(AppsRedisSubscriberActor.props(system, redisMsgReceiver), "redis-subscriber")
}
}

View File

@ -196,6 +196,7 @@ public String getJoinURL(String username, String meetingID, String record, Strin
String create_parameters = "name=" + urlEncode(meetingID)
+ "&meetingID=" + urlEncode(meetingID) + welcome_param + voiceBridge_param
+ "&attendeePW=ap&moderatorPW=mp"
+ "&isBreakoutRoom=false"
+ "&record=" + record + getMetaData( metadata );

View File

@ -1,9 +1,8 @@
name := "bbb-common-message"
organization := "org.bigbluebutton"
version := "0.0.17"
version := "0.0.18-SNAPSHOT"
// We want to have our jar files in lib_managed dir.
// This way we'll have the right path when we import
@ -16,10 +15,10 @@ testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/sc
libraryDependencies ++= {
Seq(
"com.google.code.gson" % "gson" % "1.7.1"
"com.google.code.gson" % "gson" % "2.5"
)}
libraryDependencies += "junit" % "junit" % "4.11" % "test"
libraryDependencies += "junit" % "junit" % "4.12" % "test"
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
seq(Revolver.settings: _*)
@ -47,20 +46,18 @@ autoScalaLibrary := false
* publish to the local maven repo using "sbt publish"
*/
// Uncomment this to publish to local maven repo while commenting out the nexus repo
//publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))
publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))
// Comment this out when publishing to local maven repo using SNAPSHOT version.
// To push to sonatype "sbt publishSigned"
publishTo := {
val nexus = "https://oss.sonatype.org/"
if (isSnapshot.value)
Some("snapshots" at nexus + "content/repositories/snapshots")
else
Some("releases" at nexus + "service/local/staging/deploy/maven2")
}
//publishTo := {
// val nexus = "https://oss.sonatype.org/"
// if (isSnapshot.value)
// Some("snapshots" at nexus + "content/repositories/snapshots")
// else
// Some("releases" at nexus + "service/local/staging/deploy/maven2")
//}
// Enables publishing to maven repo
publishMavenStyle := true

View File

@ -125,6 +125,12 @@ public class Constants {
public static final String PERM_LOCK_ON_JOIN = "lockOnJoin";
public static final String PERM_LOCK_ON_JOIN_CONFIG = "lockOnJoinConfigurable";
public static final String ENABLED = "enabled";
public static final String START_INDEX = "start_index";
public static final String END_INDEX = "end_index";
public static final String LOCALE = "locale";
public static final String TEXT = "text";
public static final String OWNER_ID = "owner_id";
public static final String CAPTION_HISTORY = "caption_history";
public static final String AVATAR_URL = "avatarURL";
public static final String STUNS = "stuns";
public static final String TURNS = "turns";

View File

@ -0,0 +1,74 @@
package org.bigbluebutton.common.messages;
import java.util.HashMap;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class EditCaptionHistoryMessage implements ISubscribedMessage {
public static final String EDIT_CAPTION_HISTORY = "edit_caption_history_message";
public static final String VERSION = "0.0.1";
public final String meetingID;
public final String userID;
public final Integer startIndex;
public final Integer endIndex;
public final String locale;
public final String text;
public EditCaptionHistoryMessage(String meetingID, String userID, Integer startIndex, Integer endIndex, String locale, String text) {
this.meetingID = meetingID;
this.userID = userID;
this.startIndex = startIndex;
this.endIndex = endIndex;
this.locale = locale;
this.text = text;
}
public String toJson() {
HashMap<String, Object> payload = new HashMap<String, Object>();
payload.put(Constants.MEETING_ID, meetingID);
payload.put(Constants.USER_ID, userID);
payload.put(Constants.START_INDEX, startIndex);
payload.put(Constants.END_INDEX, endIndex);
payload.put(Constants.LOCALE, locale);
payload.put(Constants.TEXT, text);
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(EDIT_CAPTION_HISTORY, VERSION, null);
return MessageBuilder.buildJson(header, payload);
}
public static EditCaptionHistoryMessage fromJson(String message) {
JsonParser parser = new JsonParser();
JsonObject obj = (JsonObject) parser.parse(message);
if (obj.has("header") && obj.has("payload")) {
JsonObject header = (JsonObject) obj.get("header");
JsonObject payload = (JsonObject) obj.get("payload");
if (header.has("name")) {
String messageName = header.get("name").getAsString();
if (EDIT_CAPTION_HISTORY.equals(messageName)) {
if (payload.has(Constants.MEETING_ID)
&& payload.has(Constants.USER_ID)
&& payload.has(Constants.START_INDEX)
&& payload.has(Constants.END_INDEX)
&& payload.has(Constants.LOCALE)
&& payload.has(Constants.TEXT)) {
String meetingID = payload.get(Constants.MEETING_ID).getAsString();
String userID = payload.get(Constants.USER_ID).getAsString();
Integer startIndex = payload.get(Constants.START_INDEX).getAsInt();
Integer endIndex = payload.get(Constants.END_INDEX).getAsInt();
String locale = payload.get(Constants.LOCALE).getAsString();
String text = payload.get(Constants.TEXT).getAsString();
return new EditCaptionHistoryMessage(meetingID, userID, startIndex, endIndex, locale, text);
}
}
}
}
return null;
}
}

View File

@ -56,6 +56,5 @@ public class EjectUserFromMeetingRequestMessage implements ISubscribedMessage {
}
}
return null;
}
}

View File

@ -1,7 +1,6 @@
package org.bigbluebutton.common.messages;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

View File

@ -29,6 +29,7 @@ public class MessagingConstants {
public static final String FROM_USERS_CHANNEL = FROM_BBB_APPS_CHANNEL + ":users";
public static final String FROM_CHAT_CHANNEL = FROM_BBB_APPS_CHANNEL + ":chat";
public static final String FROM_WHITEBOARD_CHANNEL = FROM_BBB_APPS_CHANNEL + ":whiteboard";
public static final String FROM_CAPTION_CHANNEL = FROM_BBB_APPS_CHANNEL + ":caption";
public static final String FROM_DESK_SHARE_CHANNEL = FROM_BBB_APPS_CHANNEL + ":deskshare";
public static final String TO_BBB_APPS_CHANNEL = "bigbluebutton:to-bbb-apps";
@ -41,6 +42,7 @@ public class MessagingConstants {
public static final String TO_CHAT_CHANNEL = TO_BBB_APPS_CHANNEL + ":chat";
public static final String TO_VOICE_CHANNEL = TO_BBB_APPS_CHANNEL + ":voice";
public static final String TO_WHITEBOARD_CHANNEL = TO_BBB_APPS_CHANNEL + ":whiteboard";
public static final String TO_CAPTION_CHANNEL = TO_BBB_APPS_CHANNEL + ":caption";
public static final String BBB_APPS_KEEP_ALIVE_CHANNEL = "bigbluebutton:from-bbb-apps:keepalive";

View File

@ -63,6 +63,5 @@ public class MuteUserInVoiceConfRequestMessage {
}
}
return null;
}
}

View File

@ -1,7 +1,6 @@
package org.bigbluebutton.common.messages;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

View File

@ -0,0 +1,64 @@
package org.bigbluebutton.common.messages;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class SendCaptionHistoryReplyMessage implements ISubscribedMessage {
public static final String SEND_CAPTION_HISTORY_REPLY = "send_caption_history_reply_message";
public static final String VERSION = "0.0.1";
public final String meetingID;
public final String requesterID;
public final Map<String, String[]> captionHistory;
public SendCaptionHistoryReplyMessage(String meetingID, String requesterID, Map<String, String[]> captionHistory) {
this.meetingID = meetingID;
this.captionHistory = captionHistory;
this.requesterID = requesterID;
}
public String toJson() {
HashMap<String, Object> payload = new HashMap<String, Object>();
payload.put(Constants.MEETING_ID, meetingID);
payload.put(Constants.REQUESTER_ID, requesterID);
payload.put(Constants.CHAT_HISTORY, captionHistory);
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(SEND_CAPTION_HISTORY_REPLY, VERSION, null);
return MessageBuilder.buildJson(header, payload);
}
public static SendCaptionHistoryReplyMessage fromJson(String message) {
JsonParser parser = new JsonParser();
JsonObject obj = (JsonObject) parser.parse(message);
if (obj.has("header") && obj.has("payload")) {
JsonObject header = (JsonObject) obj.get("header");
JsonObject payload = (JsonObject) obj.get("payload");
if (header.has("name")) {
String messageName = header.get("name").getAsString();
if (SEND_CAPTION_HISTORY_REPLY.equals(messageName)) {
if (payload.has(Constants.MEETING_ID)
&& payload.has(Constants.CAPTION_HISTORY)
&& payload.has(Constants.REQUESTER_ID)) {
String meetingID = payload.get(Constants.MEETING_ID).getAsString();
String requesterID = payload.get(Constants.REQUESTER_ID).getAsString();
JsonObject history = (JsonObject) payload.get(Constants.CAPTION_HISTORY);
Util util = new Util();
Map<String, String[]> captionHistory = util.extractCaptionHistory(history);
return new SendCaptionHistoryReplyMessage(meetingID, requesterID, captionHistory);
}
}
}
}
return null;
}
}

View File

@ -0,0 +1,54 @@
package org.bigbluebutton.common.messages;
import java.util.HashMap;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class SendCaptionHistoryRequestMessage implements ISubscribedMessage {
public static final String SEND_CAPTION_HISTORY_REQUEST = "send_caption_history_request_message";
public static final String VERSION = "0.0.1";
public final String meetingID;
public final String requesterID;
public SendCaptionHistoryRequestMessage(String meetingID, String requesterID) {
this.meetingID = meetingID;
this.requesterID = requesterID;
}
public String toJson() {
HashMap<String, Object> payload = new HashMap<String, Object>();
payload.put(Constants.MEETING_ID, meetingID);
payload.put(Constants.REQUESTER_ID, requesterID);
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(SEND_CAPTION_HISTORY_REQUEST, VERSION, null);
return MessageBuilder.buildJson(header, payload);
}
public static SendCaptionHistoryRequestMessage fromJson(String message) {
JsonParser parser = new JsonParser();
JsonObject obj = (JsonObject) parser.parse(message);
if (obj.has("header") && obj.has("payload")) {
JsonObject header = (JsonObject) obj.get("header");
JsonObject payload = (JsonObject) obj.get("payload");
if (header.has("name")) {
String messageName = header.get("name").getAsString();
if (SEND_CAPTION_HISTORY_REQUEST.equals(messageName)) {
if (payload.has(Constants.MEETING_ID)
&& payload.has(Constants.REQUESTER_ID)) {
String meetingID = payload.get(Constants.MEETING_ID).getAsString();
String requesterID = payload.get(Constants.REQUESTER_ID).getAsString();
return new SendCaptionHistoryRequestMessage(meetingID, requesterID);
}
}
}
}
return null;
}
}

View File

@ -1,10 +1,8 @@
package org.bigbluebutton.common.messages;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

View File

@ -0,0 +1,84 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.common.messages;
import java.util.HashMap;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class TransferUserToVoiceConfRequestMessage {
public static final String TRANSFER_USER_TO_VOICE_CONF_REQUEST = "transfer_user_to_voice_conf_request_message";
public static final String VERSION = "0.0.1";
public static final String VOICE_CONF_ID = "voice_conf_id";
public static final String TARGET_VOICE_CONF_ID = "target_voice_conf_id";
public static final String VOICE_USER_ID = "voice_user_id";
public final String voiceConfId;
public final String targetVoiceConfId;
public final String voiceUserId;
public TransferUserToVoiceConfRequestMessage(String voiceConfId,
String breakoutVoiceConfId, String voiceUserId) {
this.voiceConfId = voiceConfId;
this.targetVoiceConfId = breakoutVoiceConfId;
this.voiceUserId = voiceUserId;
}
public String toJson() {
HashMap<String, Object> payload = new HashMap<String, Object>();
payload.put(VOICE_CONF_ID, voiceConfId);
payload.put(TARGET_VOICE_CONF_ID, targetVoiceConfId);
payload.put(VOICE_USER_ID, voiceUserId);
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(
TRANSFER_USER_TO_VOICE_CONF_REQUEST, VERSION, null);
return MessageBuilder.buildJson(header, payload);
}
public static TransferUserToVoiceConfRequestMessage fromJson(String message) {
JsonParser parser = new JsonParser();
JsonObject obj = (JsonObject) parser.parse(message);
if (obj.has("header") && obj.has("payload")) {
JsonObject header = (JsonObject) obj.get("header");
JsonObject payload = (JsonObject) obj.get("payload");
if (header.has("name")) {
String messageName = header.get("name").getAsString();
if (TRANSFER_USER_TO_VOICE_CONF_REQUEST.equals(messageName)) {
if (payload.has(VOICE_CONF_ID)
&& payload.has(TARGET_VOICE_CONF_ID)
&& payload.has(VOICE_USER_ID)) {
String id = payload.get(VOICE_CONF_ID).getAsString();
String targetVoiceConfId = payload.get(
TARGET_VOICE_CONF_ID).getAsString();
String voiceUserId = payload.get(VOICE_USER_ID)
.getAsString();
return new TransferUserToVoiceConfRequestMessage(id,
targetVoiceConfId, voiceUserId);
}
}
}
}
return null;
}
}

View File

@ -0,0 +1,59 @@
package org.bigbluebutton.common.messages;
import java.util.HashMap;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class UpdateCaptionOwnerMessage implements ISubscribedMessage {
public static final String UPDATE_CAPTION_OWNER = "update_caption_owner_message";
public static final String VERSION = "0.0.1";
public final String meetingID;
public final String locale;
public final String ownerID;
public UpdateCaptionOwnerMessage(String meetingID, String locale, String ownerID) {
this.meetingID = meetingID;
this.locale = locale;
this.ownerID = ownerID;
}
public String toJson() {
HashMap<String, Object> payload = new HashMap<String, Object>();
payload.put(Constants.MEETING_ID, meetingID);
payload.put(Constants.LOCALE, locale);
payload.put(Constants.OWNER_ID, ownerID);
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(UPDATE_CAPTION_OWNER, VERSION, null);
return MessageBuilder.buildJson(header, payload);
}
public static UpdateCaptionOwnerMessage fromJson(String message) {
JsonParser parser = new JsonParser();
JsonObject obj = (JsonObject) parser.parse(message);
if (obj.has("header") && obj.has("payload")) {
JsonObject header = (JsonObject) obj.get("header");
JsonObject payload = (JsonObject) obj.get("payload");
if (header.has("name")) {
String messageName = header.get("name").getAsString();
if (UPDATE_CAPTION_OWNER.equals(messageName)) {
if (payload.has(Constants.MEETING_ID)
&& payload.has(Constants.LOCALE)
&& payload.has(Constants.OWNER_ID)) {
String meetingID = payload.get(Constants.MEETING_ID).getAsString();
String locale = payload.get(Constants.LOCALE).getAsString();
String ownerID = payload.get(Constants.OWNER_ID).getAsString();
return new UpdateCaptionOwnerMessage(meetingID, locale, ownerID);
}
}
}
}
return null;
}
}

View File

@ -717,4 +717,27 @@ public class Util {
return pollMap;
}
public Map<String, String[]> extractCaptionHistory(JsonObject history) {
Map<String, String[]> collection = new HashMap<String, String[]>();
for (Map.Entry<String,JsonElement> entry : history.entrySet()) {
String locale = entry.getKey();
JsonArray values = entry.getValue().getAsJsonArray();
String[] localeValueArray = new String[2];
int i = 0;
Iterator<JsonElement> valuesIter = values.iterator();
while (valuesIter.hasNext()){
String element = valuesIter.next().getAsString();
localeValueArray[i++] = element;
}
collection.put(locale, localeValueArray);
}
return collection;
}
}

View File

@ -0,0 +1,42 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.messages;
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
import org.bigbluebutton.messages.payload.BreakoutRoomPayload;
import com.google.gson.Gson;
public class BreakoutRoomClosed implements IBigBlueButtonMessage {
public final static String NAME = "BreakoutRoomClosed";
public final Header header;
public final BreakoutRoomPayload payload;
public BreakoutRoomClosed(BreakoutRoomPayload payload) {
header = new Header(NAME);
this.payload = payload;
}
public String toJson() {
Gson gson = new Gson();
return gson.toJson(this);
}
}

View File

@ -0,0 +1,41 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.messages;
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
import org.bigbluebutton.messages.payload.BreakoutRoomJoinURLPayload;
import com.google.gson.Gson;
public class BreakoutRoomJoinURL implements IBigBlueButtonMessage {
public final static String NAME = "BreakoutRoomJoinURL";
public final Header header;
public final BreakoutRoomJoinURLPayload payload;
public BreakoutRoomJoinURL(BreakoutRoomJoinURLPayload payload) {
header = new Header(NAME );
this.payload = payload;
}
public String toJson() {
Gson gson = new Gson();
return gson.toJson(this);
}
}

View File

@ -0,0 +1,23 @@
package org.bigbluebutton.messages;
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
import org.bigbluebutton.messages.payload.BreakoutRoomPayload;
import com.google.gson.Gson;
public class BreakoutRoomStarted implements IBigBlueButtonMessage {
public final static String NAME = "BreakoutRoomStarted";
public final Header header;
public final BreakoutRoomPayload payload;
public BreakoutRoomStarted(BreakoutRoomPayload payload) {
this.header = new Header(NAME);
this.payload = payload;
}
public String toJson() {
Gson gson = new Gson();
return gson.toJson(this);
}
}

View File

@ -0,0 +1,41 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.messages;
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
import org.bigbluebutton.messages.payload.BreakoutRoomsListPayload;
import com.google.gson.Gson;
public class BreakoutRoomsList implements IBigBlueButtonMessage {
public final static String NAME = "BreakoutRoomsList";
public final Header header;
public final BreakoutRoomsListPayload payload;
public BreakoutRoomsList(BreakoutRoomsListPayload payload) {
header = new Header(NAME);
this.payload = payload;
}
public String toJson() {
Gson gson = new Gson();
return gson.toJson(this);
}
}

View File

@ -0,0 +1,24 @@
package org.bigbluebutton.messages;
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
import org.bigbluebutton.messages.payload.BreakoutRoomsTimeRemainingPayload;
import com.google.gson.Gson;
public class BreakoutRoomsTimeRemainingUpdate implements IBigBlueButtonMessage {
public final static String NAME = "BreakoutRoomsTimeRemainingUpdate";
public final Header header;
public final BreakoutRoomsTimeRemainingPayload payload;
public BreakoutRoomsTimeRemainingUpdate(
BreakoutRoomsTimeRemainingPayload payload) {
header = new Header(NAME);
this.payload = payload;
}
public String toJson() {
Gson gson = new Gson();
return gson.toJson(this);
}
}

View File

@ -0,0 +1,23 @@
package org.bigbluebutton.messages;
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
import org.bigbluebutton.messages.payload.CreateBreakoutRoomRequestPayload;
import com.google.gson.Gson;
public class CreateBreakoutRoomRequest implements IBigBlueButtonMessage {
public final static String NAME = "CreateBreakoutRoomRequest";
public final Header header;
public final CreateBreakoutRoomRequestPayload payload;
public CreateBreakoutRoomRequest(CreateBreakoutRoomRequestPayload payload) {
this.header = new Header(NAME);
this.payload = payload;
}
public String toJson() {
Gson gson = new Gson();
return gson.toJson(this);
}
}

View File

@ -0,0 +1,28 @@
package org.bigbluebutton.messages;
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
import org.bigbluebutton.messages.payload.CreateBreakoutRoomsRequestPayload;
import com.google.gson.Gson;
/**
* The message from the client to the server requesting to create breakout
* rooms.
*/
public class CreateBreakoutRoomsRequest implements IBigBlueButtonMessage {
public final static String NAME = "CreateBreakoutRoomsRequest";
public final Header header;
public final CreateBreakoutRoomsRequestPayload payload;
public CreateBreakoutRoomsRequest(CreateBreakoutRoomsRequestPayload payload) {
this.header = new Header(NAME);
this.payload = payload;
}
public String toJson() {
Gson gson = new Gson();
return gson.toJson(this);
}
}

View File

@ -0,0 +1,50 @@
package org.bigbluebutton.messages;
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
public class CreateMeetingRequest implements IBigBlueButtonMessage {
public final static String NAME = "CreateMeetingRequest";
public final Header header;
public final CreateMeetingRequestPayload payload;
public CreateMeetingRequest(CreateMeetingRequestPayload payload) {
this.header = new Header(NAME);
this.payload = payload;
}
public static class CreateMeetingRequestPayload {
public final String id;
public final String externalId;
public final String name;
public final Boolean record;
public final String voiceConfId;
public final Integer durationInMinutes;
public final Boolean autoStartRecording;
public final Boolean allowStartStopRecording;
public final String moderatorPassword;
public final String viewerPassword;
public final Long createTime;
public final String createDate;
public final Boolean isBreakout;
public CreateMeetingRequestPayload(String id, String externalId, String name, Boolean record, String voiceConfId,
Integer duration, Boolean autoStartRecording,
Boolean allowStartStopRecording, String moderatorPass,
String viewerPass, Long createTime, String createDate, Boolean isBreakout) {
this.id = id;
this.externalId = externalId;
this.name = name;
this.record = record;
this.voiceConfId = voiceConfId;
this.durationInMinutes = duration;
this.autoStartRecording = autoStartRecording;
this.allowStartStopRecording = allowStartStopRecording;
this.moderatorPassword = moderatorPass;
this.viewerPassword = viewerPass;
this.createTime = createTime;
this.createDate = createDate;
this.isBreakout = isBreakout;
}
}
}

View File

@ -0,0 +1,41 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.messages;
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
import org.bigbluebutton.messages.payload.EndAllBreakoutRoomsRequestPayload;
import com.google.gson.Gson;
public class EndAllBreakoutRoomsRequest implements IBigBlueButtonMessage {
public final static String NAME = "EndAllBreakoutRoomsRequest";
public final Header header;
public final EndAllBreakoutRoomsRequestPayload payload;
public EndAllBreakoutRoomsRequest(EndAllBreakoutRoomsRequestPayload payload) {
this.header = new Header(NAME);
this.payload = payload;
}
public String toJson() {
Gson gson = new Gson();
return gson.toJson(this);
}
}

View File

@ -0,0 +1,41 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.messages;
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
import org.bigbluebutton.messages.payload.EndBreakoutRoomRequestPayload;
import com.google.gson.Gson;
public class EndBreakoutRoomRequest implements IBigBlueButtonMessage {
public final static String NAME = "EndBreakoutRoomRequest";
public final Header header;
public final EndBreakoutRoomRequestPayload payload;
public EndBreakoutRoomRequest(EndBreakoutRoomRequestPayload payload) {
header = new Header(NAME);
this.payload = payload;
}
public String toJson() {
Gson gson = new Gson();
return gson.toJson(this);
}
}

View File

@ -0,0 +1,41 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.messages;
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
import org.bigbluebutton.messages.payload.GetBreakoutRoomsListPayload;
import com.google.gson.Gson;
public class GetBreakoutRoomsList implements IBigBlueButtonMessage {
public final static String NAME = "GetBreakoutRoomsList";
public final Header header;
public final GetBreakoutRoomsListPayload payload;
public GetBreakoutRoomsList(GetBreakoutRoomsListPayload payload) {
header = new Header(NAME);
this.payload = payload;
}
public String toJson() {
Gson gson = new Gson();
return gson.toJson(this);
}
}

View File

@ -0,0 +1,14 @@
package org.bigbluebutton.messages;
public class Header {
public enum MessageType {
SYSTEM, BROADCAST, DIRECT
}
public final String name;
public Header(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,23 @@
package org.bigbluebutton.messages;
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
import org.bigbluebutton.messages.payload.ListenInOnBreakoutPayload;
import com.google.gson.Gson;
public class ListenInOnBreakout implements IBigBlueButtonMessage {
public final static String NAME = "ListenInOnBreakout";
public final Header header;
public final ListenInOnBreakoutPayload payload;
public ListenInOnBreakout(ListenInOnBreakoutPayload payload) {
this.header = new Header(NAME);
this.payload = payload;
}
public String toJson() {
Gson gson = new Gson();
return gson.toJson(this);
}
}

View File

@ -0,0 +1,14 @@
package org.bigbluebutton.messages;
import com.google.gson.JsonObject;
public class Message {
public final Header header;
public final JsonObject body;
public Message(Header header, JsonObject body) {
this.header = header;
this.body = body;
}
}

View File

@ -0,0 +1,6 @@
package org.bigbluebutton.messages;
public enum MessageType {
SYSTEM, DIRECT, BROADCAST
}

Some files were not shown because too many files have changed in this diff Show More