Merge pull request #3982 from ritzalam/move-java-classes-from-bbb-web-to-bbb-common-web

Merging new way of sending messages
This commit is contained in:
Richard Alam 2017-06-05 09:36:08 -04:00 committed by GitHub
commit 61a1734efb
365 changed files with 6755 additions and 2559 deletions

543
DEVELOPMENT.md Normal file → Executable file
View File

@ -1,371 +1,27 @@
This document provides instructions for developers to setup their
environment and work on the upcoming BBB 1.1 (tentative release version).
environment and work on the upcoming BBB 2.0 (tentative release version).
## Install BBB 1.0
## Install BBB 1.1
Make sure you have a working BBB 1.0 before you proceed with the instructions below.
Follow the (install instructions)[http://docs.bigbluebutton.org/install/install.html] for 1.1.
## Install OpenJDK 8
Make sure you have a working BBB 1.1 before you proceed with the instructions below.
```
sudo add-apt-repository ppa:openjdk-r/ppa
sudo apt-get update
sudo apt-get install openjdk-8-jdk
```
## Setup development environment
Change the default jre. Choose Java 8.
Setup your development environment following these (instructions)[http://docs.bigbluebutton.org/dev/setup.html]
```
sudo update-alternatives --config java
```
## Checkout development branch
Change the default jdk. Choose Jdk8
Checkout the development branch `move-java-classes-from-bbb-web-to-bbb-common-web` from this (repository)[https://github.com/ritzalam/bigbluebutton]
```
sudo update-alternatives --config javac
```
Open nine (9) terminal windows so you will dedicate one window for each bbb-component.
You can name them client, bbb-apps, apps-common, red5, akka-apps, akka-fsesl, bbb-web, common-web, and messages.
## 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.
On you bbb-client terminal, run the following commands.
```
cd ~/dev/bigbluebutton/bigbluebutton-client
@ -392,42 +48,12 @@ 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
On your red5 terminal, turn off red5 service
```
sudo service bbb-red5 stop
sudo systemctl stop red5
```
You need to make `red5/webapps` writeable. Otherwise, you will get a permission error when you try to deploy into Red5.
@ -438,77 +64,48 @@ sudo chmod -R 777 /usr/share/red5/webapps
### Build common-message
On your message terminal, run the following commands. Other components depends on this, so build this first.
```
cd ~/dev/bigbluebutton/bbb-common-message/
sbt clean
sbt publish
sbt publishLocal
```
### Build bbb-apps
We've split bbb-apps into bbb-apps-common and bigbluebutton-apps. We need to build bbb-apps-common first.
On your apps-common terminal, build the bbb-apps-common component.
```
cd ~/dev/bigbluebutton/bbb-apps-common/
# Force updating of bbb-commons-message
sbt clean
# Build and share library
sbt publish publishLocal
```
On your bbb-apps terminal, run the following commands.
```
cd ~/dev/bigbluebutton/bigbluebutton-apps/
# To make sure the lib folder is clean of old dependencies especially if you've used this
# dev environment for BBB 1.1, delete the contents of the lib directory. You can only to
# do once.
rm lib/*
# Force updating dependencies (bbb-apps-common)
gradle clean
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
gradle war deploy
```
Remove old `bbb-web` app from tomcat
@ -521,7 +118,7 @@ sudo rm /var/lib/tomcat7/webapps/bigbluebutton.war
### Run Red5
Open up a terminal.
On your red5 terminal, start red5.
```
cd /usr/share/red5
@ -530,24 +127,62 @@ sudo -u red5 ./red5.sh
### Run Akka Apps
Open up another terminal.
On your akka-apps terminal, start akka-apps
```
cd ~/dev/bigbluebutton/akka-bbb-apps
# To make sure the lib folder is clean of old dependencies especially if you've used this
# dev environment for BBB 1.1, delete the contents of the lib directory. You can only to
# do once.
rm lib_managed/*
sbt clean
sbt run
```
### Run Akka FSESL App
Open another terminal
On your akka-fsesl terminal, start akka-fsesl
```
cd ~/dev/bigbluebutton/akka-bbb-fsesl
# To make sure the lib folder is clean of old dependencies especially if you've used this
# dev environment for BBB 1.1, delete the contents of the lib directory. You can only to
# do once.
rm lib_managed/*
sbt clean
sbt run
```
### Build bbb-web
We've split up bbb-web into bbb-common-web and bigbluebutton-web. We need to build
bbb-common-web first.
On your common-web terminal, run these commands
```
cd ~/dev/bigbluebutton/bbb-common-web/
# To make sure the lib folder is clean of old dependencies especially if you've used this
# dev environment for BBB 1.1, delete the contents of the lib directory. You can only to
# do once.
rm lib_managed/*
# Force updating of dependencies especially bbb-commons-message
sbt clean
sbt publish publishLocal
```
### Run bbb-web
On your bbb-web terminal, start bbb-web
```
cd ~/dev/bigbluebutton/bigbluebutton-web
```
@ -565,8 +200,16 @@ securitySalt=856d5e0197b1aa0cf79897841142a5f6
Start bbb-web
```
# To make sure the lib folder is clean of old dependencies especially if you've used this
# dev environment for BBB 1.1, delete the contents of the lib directory. You can only to
# do once.
rm lib/*
gradle clean
gradle resolveDeps
grails clean
sudo chmod -R ugo+rwx /var/log/bigbluebutton
grails -Dserver.port=8888 run-war
```

View File

@ -6,7 +6,7 @@ organization := "org.bigbluebutton"
version := "0.0.2"
scalaVersion := "2.11.7"
scalaVersion := "2.12.2"
scalacOptions ++= Seq(
"-unchecked",
@ -14,7 +14,7 @@ scalacOptions ++= Seq(
"-Xlint",
"-Ywarn-dead-code",
"-language:_",
"-target:jvm-1.7",
"-target:jvm-1.8",
"-encoding", "UTF-8"
)
@ -37,35 +37,59 @@ testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "html", "console",
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/scalatest-reports")
val akkaVersion = "2.5.1"
val scalaTestV = "2.2.6"
libraryDependencies ++= {
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,
"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.10",
"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.19-SNAPSHOT",
"io.spray" %% "spray-json" % "1.3.2"
"org.apache.commons" % "commons-lang3" % "3.2"
)
}
libraryDependencies += "org.bigbluebutton" % "bbb-common-message_2.12" % "0.0.19-SNAPSHOT"
// https://mvnrepository.com/artifact/org.scala-lang/scala-library
libraryDependencies += "org.scala-lang" % "scala-library" % "2.12.2"
// https://mvnrepository.com/artifact/org.scala-lang/scala-compiler
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.12.2"
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-actor_2.12
libraryDependencies += "com.typesafe.akka" % "akka-actor_2.12" % "2.5.1"
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-slf4j_2.12
libraryDependencies += "com.typesafe.akka" % "akka-slf4j_2.12" % "2.5.1"
// https://mvnrepository.com/artifact/com.github.etaty/rediscala_2.12
libraryDependencies += "com.github.etaty" % "rediscala_2.12" % "1.8.0"
libraryDependencies += "com.softwaremill.quicklens" %% "quicklens" % "1.4.8"
libraryDependencies += "com.google.code.gson" % "gson" % "2.8.0"
libraryDependencies += "redis.clients" % "jedis" % "2.9.0"
libraryDependencies += "joda-time" % "joda-time" % "2.9.9"
libraryDependencies += "io.spray" % "spray-json_2.12" % "1.3.3"
libraryDependencies += "org.parboiled" % "parboiled-scala_2.12" % "1.1.8"
// https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-scala_2.12
libraryDependencies += "com.fasterxml.jackson.module" % "jackson-module-scala_2.12" % "2.8.8"
// For generating test reports
libraryDependencies += "org.pegdown" % "pegdown" % "1.6.0" % "test"
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-testkit_2.12
libraryDependencies += "com.typesafe.akka" % "akka-testkit_2.12" % "2.5.1" % "test"
// https://mvnrepository.com/artifact/org.scalactic/scalactic_2.12
libraryDependencies += "org.scalactic" % "scalactic_2.12" % "3.0.3" % "test"
// https://mvnrepository.com/artifact/org.scalatest/scalatest_2.12
libraryDependencies += "org.scalatest" % "scalatest_2.12" % "3.0.3" % "test"
libraryDependencies += "org.mockito" % "mockito-core" % "2.7.22" % "test"
seq(Revolver.settings: _*)

View File

@ -103,7 +103,8 @@ public interface IBigBlueButtonInGW {
String layout);
// Chat
void getChatHistory(String meetingID, String requesterID, String replyTo);
void getAllChatHistory(String meetingID, String requesterID, String replyTo);
void getChatHistory(String meetingID, String requesterID, String replyTo, String chatId);
void sendPublicMessage(String meetingID, String requesterID, Map<String, String> message);
void sendPrivateMessage(String meetingID, String requesterID, Map<String, String> message);
void clearPublicChatHistory(String meetingID, String requesterID);

View File

@ -30,7 +30,8 @@ public class ChatMessageReceiver implements MessageHandler{
String messageName = header.get("name").getAsString();
if (GetChatHistoryRequestMessage.GET_CHAT_HISTORY_REQUEST.equals(messageName)) {
GetChatHistoryRequestMessage msg = GetChatHistoryRequestMessage.fromJson(message);
bbbGW.getChatHistory(msg.meetingId, msg.requesterId, msg.replyTo);
// TODO: Add chatId to getChatHistory message
bbbGW.getChatHistory(msg.meetingId, msg.requesterId, msg.replyTo, "FIXME!");
} else if (SendPublicChatMessage.SEND_PUBLIC_CHAT_MESSAGE.equals(messageName)){
SendPublicChatMessage msg = SendPublicChatMessage.fromJson(message);
bbbGW.sendPublicMessage(msg.meetingId, msg.requesterId, msg.messageInfo);

View File

@ -62,3 +62,10 @@ red5 {
deskshareip="192.168.0.109"
deskshareapp="video-broadcast"
}
eventBus {
meetingManagerChannel = "MeetingManagerChannel"
outMessageChannel = "OutgoingMessageChannel"
incomingJsonMsgChannel = "IncomingJsonMsgChannel"
outBbbMsgMsgChannel = "OutBbbMsgChannel"
}

View File

@ -1,27 +1,22 @@
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 akka.event.Logging
import akka.actor.ActorSystem
import org.bigbluebutton.endpoint.redis.RedisPublisher
import org.bigbluebutton.endpoint.redis.KeepAliveRedisPublisher
import org.bigbluebutton.endpoint.redis.AppsRedisSubscriberActor
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
import org.bigbluebutton.core.pubsub.senders.ReceivedJsonMsgHandlerActor
import org.bigbluebutton.core.recorder.RecorderActor
import org.bigbluebutton.core2.FromAkkaAppsMsgSenderActor
object Boot extends App with SystemConfiguration {
@ -31,8 +26,8 @@ object Boot extends App with SystemConfiguration {
val eventBus = new IncomingEventBus
val outgoingEventBus = new OutgoingEventBus
val outGW = new OutMessageGateway(outgoingEventBus)
val outBus2 = new OutEventBus2
val outGW = new OutMessageGateway(outgoingEventBus, outBus2)
val redisPublisher = new RedisPublisher(system)
val msgSender = new MessageSender(redisPublisher)
@ -45,14 +40,23 @@ object Boot extends App with SystemConfiguration {
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")
outgoingEventBus.subscribe(messageSenderActor, outMessageChannel)
outgoingEventBus.subscribe(recorderActor, outMessageChannel)
outgoingEventBus.subscribe(newMessageSenderActor, outMessageChannel)
val incomingJsonMessageBus = new IncomingJsonMessageBus
val bbbInGW = new BigBlueButtonInGW(system, eventBus, outGW, red5DeskShareIP, red5DeskShareApp)
val bbbMsgBus = new BbbMsgRouterEventBus
val fromAkkaAppsMsgSenderActorRef = system.actorOf(FromAkkaAppsMsgSenderActor.props(msgSender))
outBus2.subscribe(fromAkkaAppsMsgSenderActorRef, outBbbMsgMsgChannel)
val bbbInGW = new BigBlueButtonInGW(system, eventBus, bbbMsgBus, outGW)
val redisMsgReceiver = new RedisMessageReceiver(bbbInGW)
val redisSubscriberActor = system.actorOf(AppsRedisSubscriberActor.props(redisMsgReceiver), "redis-subscriber")
val redisMessageHandlerActor = system.actorOf(ReceivedJsonMsgHandlerActor.props(bbbMsgBus, incomingJsonMessageBus))
incomingJsonMessageBus.subscribe(redisMessageHandlerActor, toAkkaAppsJsonChannel)
val redisSubscriberActor = system.actorOf(AppsRedisSubscriberActor.props(redisMsgReceiver, incomingJsonMessageBus), "redis-subscriber")
val keepAliveRedisPublisher = new KeepAliveRedisPublisher(system, redisPublisher)
}

View File

@ -27,4 +27,19 @@ trait SystemConfiguration {
lazy val expireLastUserLeft = Try(config.getInt("expire.lastUserLeft")).getOrElse(60) // 1 minute
lazy val expireNeverJoined = Try(config.getInt("expire.neverJoined")).getOrElse(5 * 60) // 5 minutes
lazy val meetingManagerChannel = Try(config.getString("eventBus.meetingManagerChannel")).getOrElse("MeetingManagerChannel")
lazy val outMessageChannel = Try(config.getString("eventBus.outMessageChannel")).getOrElse("OutgoingMessageChannel")
lazy val incomingJsonMsgChannel = Try(config.getString("eventBus.incomingJsonMsgChannel")).getOrElse("IncomingJsonMsgChannel")
lazy val outBbbMsgMsgChannel = Try(config.getString("eventBus.outBbbMsgMsgChannel")).getOrElse("OutBbbMsgChannel")
lazy val toAkkaAppsRedisChannel = Try(config.getString("redis.toAkkaAppsRedisChannel")).getOrElse("to-akka-apps-redis-channel")
lazy val fromAkkaAppsRedisChannel = Try(config.getString("redis.fromAkkaAppsRedisChannel")).getOrElse("from-akka-apps-redis-channel")
lazy val fromAkkaAppsChannel = Try(config.getString("eventBus.fromAkkaAppsChannel")).getOrElse("from-akka-apps-channel")
lazy val toAkkaAppsChannel = Try(config.getString("eventBus.toAkkaAppsChannel")).getOrElse("to-akka-apps-channel")
lazy val fromClientChannel = Try(config.getString("eventBus.fromClientChannel")).getOrElse("from-client-channel")
lazy val toClientChannel = Try(config.getString("eventBus.toClientChannel")).getOrElse("to-client-channel")
lazy val toAkkaAppsJsonChannel = Try(config.getString("eventBus.toAkkaAppsChannel")).getOrElse("to-akka-apps-json-channel")
lazy val fromAkkaAppsJsonChannel = Try(config.getString("eventBus.fromAkkaAppsChannel")).getOrElse("from-akka-apps-json-channel")
lazy val fromAkkaAppsOldJsonChannel = Try(config.getString("eventBus.fromAkkaAppsOldChannel")).getOrElse("from-akka-apps-old-json-channel")
}

View File

@ -1,10 +1,10 @@
package org.bigbluebutton.core
import java.io.{ PrintWriter, StringWriter }
import akka.actor._
import akka.actor.ActorLogging
import akka.actor.SupervisorStrategy.Resume
import akka.pattern.{ ask, pipe }
import akka.util.Timeout
import scala.concurrent.duration._
@ -13,22 +13,29 @@ import org.bigbluebutton.core.api._
import org.bigbluebutton.SystemConfiguration
import java.util.concurrent.TimeUnit
import org.bigbluebutton.common2.messages.MessageBody.MeetingCreatedEvtBody
import org.bigbluebutton.common2.messages._
import org.bigbluebutton.core.running.RunningMeeting
import org.bigbluebutton.core2.RunningMeetings
import org.bigbluebutton.core2.message.handlers.CreateMeetingReqMsgHdlr
object BigBlueButtonActor extends SystemConfiguration {
def props(system: ActorSystem,
eventBus: IncomingEventBus,
bbbMsgBus: BbbMsgRouterEventBus,
outGW: OutMessageGateway): Props =
Props(classOf[BigBlueButtonActor], system, eventBus, outGW)
Props(classOf[BigBlueButtonActor], system, eventBus, bbbMsgBus, outGW)
}
class BigBlueButtonActor(val system: ActorSystem,
eventBus: IncomingEventBus, outGW: OutMessageGateway) extends Actor with ActorLogging {
val eventBus: IncomingEventBus, val bbbMsgBus: BbbMsgRouterEventBus,
val outGW: OutMessageGateway) extends Actor
with ActorLogging with SystemConfiguration {
implicit def executionContext = system.dispatcher
implicit val timeout = Timeout(5 seconds)
private var meetings = new collection.immutable.HashMap[String, RunningMeeting]
private val meetings = new RunningMeetings
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case e: Exception => {
@ -40,8 +47,19 @@ class BigBlueButtonActor(val system: ActorSystem,
}
}
override def preStart() {
bbbMsgBus.subscribe(self, meetingManagerChannel)
}
override def postStop() {
bbbMsgBus.unsubscribe(self, meetingManagerChannel)
}
def receive = {
case msg: CreateMeeting => handleCreateMeeting(msg)
// 2x messages
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
// 1x messages
case msg: DestroyMeeting => handleDestroyMeeting(msg)
case msg: KeepAliveMessage => handleKeepAliveMessage(msg)
case msg: PubSubPing => handlePubSubPingMessage(msg)
@ -56,8 +74,71 @@ class BigBlueButtonActor(val system: ActorSystem,
case _ => // do nothing
}
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
msg.core match {
case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m)
case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m)
case _ => println("***** Cannot handle " + msg.envelope.name)
}
}
def handleRegisterUserReqMsg(msg: RegisterUserReqMsg): Unit = {
log.debug("****** RECEIVED RegisterUserReqMsg msg {}", msg)
for {
m <- RunningMeetings.findWithId(meetings, msg.header.meetingId)
} yield {
m.actorRef forward (msg)
}
}
def handleCreateMeetingReqMsg(msg: CreateMeetingReqMsg): Unit = {
log.debug("****** RECEIVED CreateMeetingReqMsg msg {}", msg)
RunningMeetings.findWithId(meetings, msg.body.props.meetingProp.intId) match {
case None => {
log.info("Create meeting request. meetingId={}", msg.body.props.meetingProp.intId)
val m = RunningMeeting(msg.body.props, outGW, eventBus)
/** Subscribe to meeting and voice events. **/
eventBus.subscribe(m.actorRef, m.props.meetingProp.intId)
eventBus.subscribe(m.actorRef, m.props.voiceProp.voiceConf)
eventBus.subscribe(m.actorRef, m.props.screenshareProps.screenshareConf)
bbbMsgBus.subscribe(m.actorRef, m.props.meetingProp.intId)
RunningMeetings.add(meetings, m)
// Send old message format
outGW.send(new MeetingCreated(m.props.meetingProp.intId,
m.props.meetingProp.extId, m.props.breakoutProps.parentId,
m.props.recordProp.record, m.props.meetingProp.name,
m.props.voiceProp.voiceConf, m.props.durationProps.duration,
m.props.password.moderatorPass, m.props.password.viewerPass,
m.props.durationProps.createdTime, m.props.durationProps.createdDate,
m.props.meetingProp.isBreakout))
m.actorRef ! new InitializeMeeting(m.props.meetingProp.intId, m.props.recordProp.record)
// Send new 2x message
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(MeetingCreatedEvtMsg.NAME, routing)
val header = BbbCoreBaseHeader(MeetingCreatedEvtMsg.NAME)
val body = MeetingCreatedEvtBody(msg.body.props)
val event = MeetingCreatedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
case Some(m) => {
log.info("Meeting already created. meetingID={}", msg.body.props.meetingProp.intId)
// do nothing
}
}
}
private def findMeetingWithVoiceConfId(voiceConfId: String): Option[RunningMeeting] = {
meetings.values.find(m => { m.mProps.voiceBridge == voiceConfId })
RunningMeetings.findMeetingWithVoiceConfId(meetings, voiceConfId)
}
private def handleUserJoinedVoiceConfMessage(msg: UserJoinedVoiceConfMessage) {
@ -96,25 +177,31 @@ class BigBlueButtonActor(val system: ActorSystem,
}
private def handleValidateAuthToken(msg: ValidateAuthToken) {
meetings.get(msg.meetingID) foreach { m =>
m.actorRef ! msg
// 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))
// }
// }
for {
m <- RunningMeetings.findWithId(meetings, msg.meetingID)
} yield {
m.actorRef forward (msg)
}
//meetings.get(msg.meetingID) foreach { m =>
// m.actorRef ! msg
// 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))
// }
// }
//}
}
private def handleKeepAliveMessage(msg: KeepAliveMessage): Unit = {
@ -127,6 +214,41 @@ class BigBlueButtonActor(val system: ActorSystem,
private def handleDestroyMeeting(msg: DestroyMeeting) {
log.info("Received DestroyMeeting message for meetingId={}", msg.meetingID)
for {
m <- RunningMeetings.findWithId(meetings, msg.meetingID)
m2 <- RunningMeetings.remove(meetings, msg.meetingID)
} yield {
log.info("Kick everyone out on meetingId={}", msg.meetingID)
if (m.props.meetingProp.isBreakout) {
log.info("Informing parent meeting {} that a breakout room has been ended {}",
m.props.breakoutProps.parentId, m.props.meetingProp.intId)
eventBus.publish(BigBlueButtonEvent(m.props.breakoutProps.parentId,
BreakoutRoomEnded(m.props.breakoutProps.parentId, m.props.meetingProp.intId)))
}
// Eject all users using the client.
outGW.send(new EndAndKickAll(msg.meetingID, m.props.recordProp.record))
// Eject all users from the voice conference
outGW.send(new EjectAllVoiceUsers(msg.meetingID, m.props.recordProp.record, m.props.voiceProp.voiceConf))
// Delay sending DisconnectAllUsers because of RTMPT connection being dropped before UserEject message arrives to the client
context.system.scheduler.scheduleOnce(Duration.create(2500, TimeUnit.MILLISECONDS)) {
// Disconnect all clients
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.props.meetingProp.intId)
eventBus.unsubscribe(m.actorRef, m.props.voiceProp.voiceConf)
// Stop the meeting actor.
context.stop(m.actorRef)
}
}
/*
meetings.get(msg.meetingID) match {
case None => log.info("Could not find meetingId={}", msg.meetingID)
case Some(m) => {
@ -150,7 +272,7 @@ class BigBlueButtonActor(val system: ActorSystem,
log.info("Destroyed meetingId={}", msg.meetingID)
outGW.send(new MeetingDestroyed(msg.meetingID))
/** Unsubscribe to meeting and voice events. **/
// Unsubscribe to meeting and voice events.
eventBus.unsubscribe(m.actorRef, m.mProps.meetingID)
eventBus.unsubscribe(m.actorRef, m.mProps.voiceBridge)
@ -159,45 +281,20 @@ class BigBlueButtonActor(val system: ActorSystem,
}
}
}
}
private def handleCreateMeeting(msg: CreateMeeting): Unit = {
meetings.get(msg.meetingID) match {
case None => {
log.info("Create meeting request. meetingId={}", msg.mProps.meetingID)
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)
eventBus.subscribe(m.actorRef, m.mProps.deskshareBridge)
meetings += m.mProps.meetingID -> m
outGW.send(new MeetingCreated(m.mProps.meetingID, m.mProps.externalMeetingID, m.mProps.parentMeetingID,
m.mProps.recorded, m.mProps.meetingName, m.mProps.voiceBridge, msg.mProps.duration, msg.mProps.moderatorPass,
msg.mProps.viewerPass, msg.mProps.createTime, msg.mProps.createDate, msg.mProps.isBreakout))
m.actorRef ! new InitializeMeeting(m.mProps.meetingID, m.mProps.recorded)
}
case Some(m) => {
log.info("Meeting already created. meetingID={}", msg.mProps.meetingID)
// do nothing
}
}
*/
}
private def handleGetAllMeetingsRequest(msg: GetAllMeetingsRequest) {
val len = meetings.keys.size
val len = RunningMeetings.numMeetings(meetings)
var currPosition = len - 1
var resultArray: Array[MeetingInfo] = new Array[MeetingInfo](len)
val resultArray: Array[MeetingInfo] = new Array[MeetingInfo](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
RunningMeetings.meetings(meetings).foreach(m => {
val id = m.props.meetingProp.intId
val duration = m.props.durationProps.duration
val name = m.props.meetingProp.name
val recorded = m.props.recordProp.record
val voiceBridge = m.props.voiceProp.voiceConf
val info = new MeetingInfo(id, name, recorded, voiceBridge, duration)
resultArray(currPosition) = info
@ -212,7 +309,7 @@ class BigBlueButtonActor(val system: ActorSystem,
eventBus.publish(BigBlueButtonEvent(id, new GetPresentationInfo(id, html5clientRequesterID, html5clientRequesterID)))
//send chat history
eventBus.publish(BigBlueButtonEvent(id, new GetChatHistoryRequest(id, html5clientRequesterID, html5clientRequesterID)))
eventBus.publish(BigBlueButtonEvent(id, new GetAllChatHistoryRequest(id, html5clientRequesterID, html5clientRequesterID)))
//send lock settings
eventBus.publish(BigBlueButtonEvent(id, new GetLockSettings(id, html5clientRequesterID)))

View File

@ -2,6 +2,7 @@ package org.bigbluebutton.core
import org.bigbluebutton.core.bus._
import org.bigbluebutton.core.api._
import scala.collection.JavaConversions._
import org.bigbluebutton.core.apps.Page
import org.bigbluebutton.core.apps.Presentation
@ -12,17 +13,20 @@ import org.bigbluebutton.common.messages.StartCustomPollRequestMessage
import org.bigbluebutton.common.messages.PubSubPingMessage
import org.bigbluebutton.messages._
import akka.event.Logging
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.core.models.Roles
class BigBlueButtonInGW(
val system: ActorSystem,
eventBus: IncomingEventBus,
outGW: OutMessageGateway,
val red5DeskShareIP: String,
val red5DeskShareApp: String) extends IBigBlueButtonInGW {
bbbMsgBus: BbbMsgRouterEventBus,
outGW: OutMessageGateway) extends IBigBlueButtonInGW with SystemConfiguration {
val log = Logging(system, getClass)
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, eventBus, outGW), "bigbluebutton-actor")
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, eventBus, bbbMsgBus, outGW), "bigbluebutton-actor")
eventBus.subscribe(bbbActor, meetingManagerChannel)
/** For OLD Messaged **/
eventBus.subscribe(bbbActor, "meeting-manager")
def handleBigBlueButtonMessage(message: IBigBlueButtonMessage) {
@ -46,7 +50,7 @@ class BigBlueButtonInGW(
//default
case undef => GuestPolicy.ASK_MODERATOR
}
/*
val mProps = new MeetingProperties(
msg.payload.id,
msg.payload.externalId,
@ -71,6 +75,7 @@ class BigBlueButtonInGW(
)
eventBus.publish(BigBlueButtonEvent("meeting-manager", new CreateMeeting(msg.payload.id, mProps)))
*/
}
}
}
@ -157,8 +162,8 @@ class BigBlueButtonInGW(
val disablePrivChat = s.getOrElse("disablePrivateChat", false)
val disablePubChat = s.getOrElse("disablePublicChat", false)
val lockedLayout = s.getOrElse("lockedLayout", false)
var lockOnJoin = s.getOrElse("lockOnJoin", false)
var lockOnJoinConfigurable = s.getOrElse("lockOnJoinConfigurable", false)
val lockOnJoin = s.getOrElse("lockOnJoin", false)
val lockOnJoinConfigurable = s.getOrElse("lockOnJoinConfigurable", false)
val permissions = new Permissions(disableCam = disableCam,
disableMic = disableMic,
@ -331,22 +336,21 @@ class BigBlueButtonInGW(
def generatePresentationPages(presId: String, numPages: Int, presBaseUrl: String): scala.collection.immutable.HashMap[String, Page] = {
var pages = new scala.collection.immutable.HashMap[String, Page]
val baseUrl =
for (i <- 1 to numPages) {
val id = presId + "/" + i
val num = i;
val current = if (i == 1) true else false
val thumbnail = presBaseUrl + "/thumbnail/" + i
val swfUri = presBaseUrl + "/slide/" + i
for (i <- 1 to numPages) {
val id = presId + "/" + i
val num = i;
val current = if (i == 1) true else false
val thumbnail = presBaseUrl + "/thumbnail/" + i
val swfUri = presBaseUrl + "/slide/" + i
val txtUri = presBaseUrl + "/textfiles/" + i
val svgUri = presBaseUrl + "/svg/" + i
val txtUri = presBaseUrl + "/textfiles/" + i
val svgUri = presBaseUrl + "/svg/" + i
val p = new Page(id = id, num = num, thumbUri = thumbnail, swfUri = swfUri,
txtUri = txtUri, svgUri = svgUri,
current = current)
pages += (p.id -> p)
}
val p = new Page(id = id, num = num, thumbUri = thumbnail, swfUri = swfUri,
txtUri = txtUri, svgUri = svgUri,
current = current)
pages += (p.id -> p)
}
pages
}
@ -413,8 +417,12 @@ class BigBlueButtonInGW(
* *****************************************************************
*/
def getChatHistory(meetingID: String, requesterID: String, replyTo: String) {
eventBus.publish(BigBlueButtonEvent(meetingID, new GetChatHistoryRequest(meetingID, requesterID, replyTo)))
def getAllChatHistory(meetingID: String, requesterID: String, replyTo: String) {
eventBus.publish(BigBlueButtonEvent(meetingID, new GetAllChatHistoryRequest(meetingID, requesterID, replyTo)))
}
def getChatHistory(meetingID: String, requesterID: String, replyTo: String, chatId: String) {
eventBus.publish(BigBlueButtonEvent(meetingID, new GetChatHistoryRequest(meetingID, requesterID, replyTo, chatId)))
}
def sendPublicMessage(meetingID: String, requesterID: String, message: java.util.Map[String, String]) {

View File

@ -1,7 +1,6 @@
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._

View File

@ -1,29 +1,10 @@
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._

View File

@ -7,12 +7,6 @@ import java.util.concurrent.TimeUnit
import org.bigbluebutton.core.api.GuestPolicy.GuestPolicy
case object StopMeetingActor
case class MeetingProperties(meetingID: String, externalMeetingID: String, parentMeetingID: String, meetingName: String,
recorded: Boolean, voiceBridge: String, deskshareBridge: String, duration: Int,
autoStartRecording: Boolean, allowStartStopRecording: Boolean, webcamsOnlyForModerator: Boolean,
moderatorPass: String, viewerPass: String, createTime: Long, createDate: String,
red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean, sequence: Int,
metadata: collection.immutable.Map[String, String], guestPolicy: GuestPolicy)
case class MeetingExtensionProp(maxExtensions: Int = 2, numExtensions: Int = 0, extendByMinutes: Int = 20,
sendNotice: Boolean = true, sent15MinNotice: Boolean = false,
@ -22,9 +16,9 @@ class MeetingModel {
private var audioSettingsInited = false
private var permissionsInited = false
private var permissions = new Permissions()
private var recording = false;
private var recording = false
private var broadcastingRTMP = false
private var muted = false;
private var muted = false
private var meetingEnded = false
private var meetingMuted = false
private var guestPolicy = GuestPolicy.ASK_MODERATOR
@ -40,104 +34,103 @@ class MeetingModel {
private var desktopShareVideoHeight = 0
private var extension = new MeetingExtensionProp
/*
val startedOn = timeNowInSeconds;
val startedOn = timeNowInSeconds;
var breakoutRoomsStartedOn: Long = 0
var breakoutRoomsdurationInMinutes: Int = 0
var breakoutRoomsStartedOn: Long = 0;
var breakoutRoomsdurationInMinutes: Int = 0;
def resetDesktopSharingParams() = {
broadcastingRTMP = false
deskShareStarted = false
rtmpBroadcastingUrl = ""
desktopShareVideoWidth = 0
desktopShareVideoHeight = 0
}
def getDeskShareStarted(): Boolean = {
return deskShareStarted
}
def setDeskShareStarted(b: Boolean) {
deskShareStarted = b
println("---deskshare status changed to:" + b)
}
def setDesktopShareVideoWidth(videoWidth: Int) {
desktopShareVideoWidth = videoWidth
}
def setDesktopShareVideoHeight(videoHeight: Int) {
desktopShareVideoHeight = videoHeight
}
def getDesktopShareVideoWidth(): Int = {
desktopShareVideoWidth
}
def getDesktopShareVideoHeight(): Int = {
desktopShareVideoHeight
}
def broadcastingRTMPStarted() {
broadcastingRTMP = true
}
def isBroadcastingRTMP(): Boolean = {
broadcastingRTMP
}
def broadcastingRTMPStopped() {
broadcastingRTMP = false
}
def setRTMPBroadcastingUrl(path: String) {
println("---RTMP broadcastUrl changed to:" + path)
rtmpBroadcastingUrl = path
}
def getRTMPBroadcastingUrl(): String = {
rtmpBroadcastingUrl
}
def isExtensionAllowed(): Boolean = extension.numExtensions < extension.maxExtensions
def incNumExtension(): Int = {
if (extension.numExtensions < extension.maxExtensions) {
extension = extension.copy(numExtensions = extension.numExtensions + 1); extension.numExtensions
def resetDesktopSharingParams() = {
broadcastingRTMP = false
deskShareStarted = false
rtmpBroadcastingUrl = ""
desktopShareVideoWidth = 0
desktopShareVideoHeight = 0
}
extension.numExtensions
}
def notice15MinutesSent() = extension = extension.copy(sent15MinNotice = true)
def notice10MinutesSent() = extension = extension.copy(sent10MinNotice = true)
def notice5MinutesSent() = extension = extension.copy(sent5MinNotice = true)
def getDeskShareStarted(): Boolean = {
return deskShareStarted
}
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())
def getGuestPolicy(): GuestPolicy.GuestPolicy = guestPolicy
def setGuestPolicy(policy: GuestPolicy.GuestPolicy) = guestPolicy = policy
def getGuestPolicySetBy(): String = guestPolicySetBy
def setGuestPolicySetBy(user: String) = guestPolicySetBy = user
def setDeskShareStarted(b: Boolean) {
deskShareStarted = b
}
def setDesktopShareVideoWidth(videoWidth: Int) {
desktopShareVideoWidth = videoWidth
}
def setDesktopShareVideoHeight(videoHeight: Int) {
desktopShareVideoHeight = videoHeight
}
def getDesktopShareVideoWidth(): Int = {
desktopShareVideoWidth
}
def getDesktopShareVideoHeight(): Int = {
desktopShareVideoHeight
}
def broadcastingRTMPStarted() {
broadcastingRTMP = true
}
def isBroadcastingRTMP(): Boolean = {
broadcastingRTMP
}
def broadcastingRTMPStopped() {
broadcastingRTMP = false
}
def setRTMPBroadcastingUrl(path: String) {
rtmpBroadcastingUrl = path
}
def getRTMPBroadcastingUrl(): String = {
rtmpBroadcastingUrl
}
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 notice15MinutesSent() = extension = extension.copy(sent15MinNotice = true)
def notice10MinutesSent() = extension = extension.copy(sent10MinNotice = true)
def notice5MinutesSent() = extension = extension.copy(sent5MinNotice = true)
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())
def getGuestPolicy(): GuestPolicy.GuestPolicy = guestPolicy
def setGuestPolicy(policy: GuestPolicy.GuestPolicy) = guestPolicy = policy
def getGuestPolicySetBy(): String = guestPolicySetBy
def setGuestPolicySetBy(user: String) = guestPolicySetBy = user
*/
}

View File

@ -1,7 +1,6 @@
package org.bigbluebutton.core
import akka.actor.Actor
import akka.actor.ActorRef
import akka.actor.ActorLogging
import akka.actor.Props
import akka.actor.OneForOneStrategy
@ -20,8 +19,6 @@ import org.bigbluebutton.common.messages.GetPresentationInfoReplyMessage
import org.bigbluebutton.common.messages.PresentationRemovedMessage
import org.bigbluebutton.common.messages.AllowUserToShareDesktopReply
import org.bigbluebutton.core.apps.Page
import collection.JavaConverters._
import scala.collection.JavaConversions._
import scala.concurrent.duration._
import org.bigbluebutton.core.apps.SimplePollResultOutVO
@ -38,7 +35,6 @@ 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
import org.bigbluebutton.core
object MessageSenderActor {
def props(msgSender: MessageSender): Props =
@ -705,6 +701,7 @@ class MessageSenderActor(val service: MessageSender)
private def handleUserJoined(msg: UserJoined) {
val json = UsersMessageToJsonConverter.userJoinedToJson(msg)
//println("************** Publishing [" + json + "] *******************")
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
}

View File

@ -1,19 +1,23 @@
package org.bigbluebutton.core
import akka.actor.ActorRef
import akka.actor.ActorContext
import org.bigbluebutton.core.bus.OutgoingEventBus
import org.bigbluebutton.core.bus.BigBlueButtonOutMessage
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.common2.messages.{ BbbCommonEnvCoreMsg }
import org.bigbluebutton.core.bus.{ BbbOutMessage, BigBlueButtonOutMessage, OutEventBus2, OutgoingEventBus }
import org.bigbluebutton.core.api.IOutMessage
object OutMessageGateway {
def apply(outgoingEventBus: OutgoingEventBus) =
new OutMessageGateway(outgoingEventBus)
def apply(outgoingEventBus: OutgoingEventBus, outBus2: OutEventBus2) =
new OutMessageGateway(outgoingEventBus, outBus2)
}
class OutMessageGateway(outgoingEventBus: OutgoingEventBus) {
class OutMessageGateway(outgoingEventBus: OutgoingEventBus, outBus2: OutEventBus2) extends SystemConfiguration {
def send(msg: IOutMessage) {
outgoingEventBus.publish(BigBlueButtonOutMessage("outgoingMessageChannel", msg))
outgoingEventBus.publish(BigBlueButtonOutMessage(outMessageChannel, msg))
}
}
def send(msg: BbbCommonEnvCoreMsg): Unit = {
println("****** Sending to outBbbMsgMsgChannel " + msg.envelope.name)
outBus2.publish(BbbOutMessage(outBbbMsgMsgChannel, msg))
}
}

View File

@ -1,19 +1,16 @@
package org.bigbluebutton.core
import akka.actor.Actor
import akka.actor.ActorRef
import akka.actor.ActorLogging
import akka.actor.Props
import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy.Resume
import java.io.{ PrintWriter, StringWriter }
import org.bigbluebutton.core.api._
import java.util.concurrent.TimeUnit
import org.bigbluebutton.core.util._
import org.bigbluebutton.core.recorder.RecorderActor
import scala.concurrent.duration._
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
object OutMessageGatewayActor {

View File

@ -4,7 +4,6 @@ import org.bigbluebutton.core.api.GuestPolicy._
import org.bigbluebutton.core.api.SharedNotesOperation._
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
@ -34,7 +33,6 @@ case class KeepAliveMessage(aliveID: String) extends InMessage
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
@ -103,7 +101,8 @@ case class LogoutEndMeeting(meetingID: String, userID: String) extends InMessage
// Chat
/////////////////////////////////////////////////////////////////////////////////
case class GetChatHistoryRequest(meetingID: String, requesterID: String, replyTo: String) extends InMessage
case class GetAllChatHistoryRequest(meetingID: String, requesterID: String, replyTo: String) extends InMessage
case class GetChatHistoryRequest(meetingID: String, requesterID: String, replyTo: String, chatId: 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
case class ClearPublicChatHistoryRequest(meetingID: String, requesterID: String) extends InMessage
@ -221,4 +220,4 @@ case class GetCurrentDocumentRequest(meetingID: String, requesterID: String) ext
case class CreateAdditionalNotesRequest(meetingID: String, requesterID: String, noteName: String) extends InMessage
case class DestroyAdditionalNotesRequest(meetingID: String, requesterID: String, noteID: String) extends InMessage
case class RequestAdditionalNotesSetRequest(meetingID: String, requesterID: String, additionalNotesSetSize: Int) extends InMessage
case class SharedNotesSyncNoteRequest(meetingID: String, requesterID: String, noteID: String) extends InMessage
case class SharedNotesSyncNoteRequest(meetingID: String, requesterID: String, noteID: String) extends InMessage

View File

@ -11,7 +11,8 @@ import org.bigbluebutton.core.api._
import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.bus.IncomingEventBus
import org.bigbluebutton.core.models.Users
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor }
import org.bigbluebutton.core.running.MeetingActor
import org.bigbluebutton.core2.MeetingStatus2x
trait BreakoutRoomApp extends SystemConfiguration {
this: MeetingActor =>
@ -22,18 +23,20 @@ trait BreakoutRoomApp extends SystemConfiguration {
def handleBreakoutRoomsList(msg: BreakoutRoomsListMessage) {
val breakoutRooms = liveMeeting.breakoutModel.getRooms().toVector map { r => new BreakoutRoomBody(r.name, r.externalMeetingId, r.id, r.sequence) }
val roomsReady = liveMeeting.breakoutModel.pendingRoomsNumber == 0 && breakoutRooms.length > 0
log.info("Sending breakout rooms list to {} with containing {} room(s)", mProps.meetingID, breakoutRooms.length)
outGW.send(new BreakoutRoomsListOutMessage(mProps.meetingID, breakoutRooms, roomsReady))
log.info("Sending breakout rooms list to {} with containing {} room(s)", props.meetingProp.intId, breakoutRooms.length)
outGW.send(new BreakoutRoomsListOutMessage(props.meetingProp.intId, breakoutRooms, roomsReady))
}
def handleCreateBreakoutRooms(msg: CreateBreakoutRooms) {
// If breakout rooms are being created we ignore the coming message
if (liveMeeting.breakoutModel.pendingRoomsNumber > 0) {
log.warning("CreateBreakoutRooms event received while {} are pending to be created for meeting {}", liveMeeting.breakoutModel.pendingRoomsNumber, mProps.meetingID)
log.warning("CreateBreakoutRooms event received while {} are pending to be created for meeting {}",
liveMeeting.breakoutModel.pendingRoomsNumber, props.meetingProp.intId)
return
}
if (liveMeeting.breakoutModel.getNumberOfRooms() > 0) {
log.warning("CreateBreakoutRooms event received while {} breakout rooms running for meeting {}", liveMeeting.breakoutModel.getNumberOfRooms(), mProps.meetingID)
log.warning("CreateBreakoutRooms event received while {} breakout rooms running for meeting {}",
liveMeeting.breakoutModel.getNumberOfRooms(), props.meetingProp.intId)
return
}
@ -46,17 +49,18 @@ trait BreakoutRoomApp extends SystemConfiguration {
for (room <- msg.rooms) {
i += 1
val breakoutMeetingId = BreakoutRoomsUtil.createMeetingIds(mProps.meetingID, i)
val voiceConfId = BreakoutRoomsUtil.createVoiceConfId(mProps.voiceBridge, i)
val r = liveMeeting.breakoutModel.createBreakoutRoom(mProps.meetingID, breakoutMeetingId._1, breakoutMeetingId._2, room.name,
val breakoutMeetingId = BreakoutRoomsUtil.createMeetingIds(props.meetingProp.intId, i)
val voiceConfId = BreakoutRoomsUtil.createVoiceConfId(props.voiceProp.voiceConf, i)
val r = liveMeeting.breakoutModel.createBreakoutRoom(props.meetingProp.intId, breakoutMeetingId._1, breakoutMeetingId._2, room.name,
room.sequence, voiceConfId, room.users)
val p = new BreakoutRoomOutPayload(r.id, r.name, mProps.meetingID, r.sequence,
r.voiceConfId, msg.durationInMinutes, mProps.moderatorPass, mProps.viewerPass,
val p = new BreakoutRoomOutPayload(r.id, r.name, props.meetingProp.intId, r.sequence,
r.voiceConfId, msg.durationInMinutes, props.password.moderatorPass, props.password.viewerPass,
sourcePresentationId, sourcePresentationSlide, msg.record)
outGW.send(new CreateBreakoutRoom(mProps.meetingID, p))
outGW.send(new CreateBreakoutRoom(props.meetingProp.intId, p))
}
liveMeeting.meetingModel.breakoutRoomsdurationInMinutes = msg.durationInMinutes;
liveMeeting.meetingModel.breakoutRoomsStartedOn = liveMeeting.timeNowInSeconds;
MeetingStatus2x.breakoutRoomsdurationInMinutes(liveMeeting.status, msg.durationInMinutes)
MeetingStatus2x.breakoutRoomsStartedOn(liveMeeting.status, MeetingStatus2x.timeNowInSeconds)
}
def sendJoinURL(userId: String, externalMeetingId: String, roomSequence: String) {
@ -64,14 +68,18 @@ trait BreakoutRoomApp extends SystemConfiguration {
for {
user <- Users.findWithId(userId, liveMeeting.users)
apiCall = "join"
params = BreakoutRoomsUtil.joinParams(user.name, userId + "-" + roomSequence, true, externalMeetingId, mProps.moderatorPass)
params = BreakoutRoomsUtil.joinParams(user.name, userId + "-" + roomSequence, true,
externalMeetingId, props.password.moderatorPass)
// We generate a first url with redirect -> true
redirectBaseString = BreakoutRoomsUtil.createBaseString(params._1)
redirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectBaseString, BreakoutRoomsUtil.calculateChecksum(apiCall, redirectBaseString, bbbWebSharedSecret))
redirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectBaseString,
BreakoutRoomsUtil.calculateChecksum(apiCall, redirectBaseString, bbbWebSharedSecret))
// We generate a second url with redirect -> false
noRedirectBaseString = BreakoutRoomsUtil.createBaseString(params._2)
noRedirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, noRedirectBaseString, BreakoutRoomsUtil.calculateChecksum(apiCall, noRedirectBaseString, bbbWebSharedSecret))
} yield outGW.send(new BreakoutRoomJoinURLOutMessage(mProps.meetingID, mProps.recorded, externalMeetingId, userId, redirectJoinURL, noRedirectJoinURL))
noRedirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, noRedirectBaseString,
BreakoutRoomsUtil.calculateChecksum(apiCall, noRedirectBaseString, bbbWebSharedSecret))
} yield outGW.send(new BreakoutRoomJoinURLOutMessage(props.meetingProp.intId,
props.recordProp.record, externalMeetingId, userId, redirectJoinURL, noRedirectJoinURL))
}
def handleRequestBreakoutJoinURL(msg: RequestBreakoutJoinURLInMessage) {
@ -89,7 +97,7 @@ trait BreakoutRoomApp extends SystemConfiguration {
// We postpone sending invitation until all breakout rooms have been created
if (liveMeeting.breakoutModel.pendingRoomsNumber == 0) {
log.info("All breakout rooms created for meetingId={}", mProps.meetingID)
log.info("All breakout rooms created for meetingId={}", props.meetingProp.intId)
liveMeeting.breakoutModel.getRooms().foreach { room =>
liveMeeting.breakoutModel.getAssignedUsers(room.id) foreach { users =>
users.foreach { u =>
@ -98,13 +106,15 @@ trait BreakoutRoomApp extends SystemConfiguration {
}
}
}
handleBreakoutRoomsList(new BreakoutRoomsListMessage(mProps.meetingID))
handleBreakoutRoomsList(new BreakoutRoomsListMessage(props.meetingProp.intId))
}
}
def sendBreakoutRoomStarted(meetingId: String, breakoutName: String, externalMeetingId: String, breakoutMeetingId: String, sequence: Int, voiceConfId: String) {
def sendBreakoutRoomStarted(meetingId: String, breakoutName: String, externalMeetingId: String,
breakoutMeetingId: String, sequence: Int, voiceConfId: String) {
log.info("Sending breakout room started {} for parent meeting {} ", breakoutMeetingId, meetingId);
outGW.send(new BreakoutRoomStartedOutMessage(meetingId, mProps.recorded, new BreakoutRoomBody(breakoutName, externalMeetingId, breakoutMeetingId, sequence)))
outGW.send(new BreakoutRoomStartedOutMessage(meetingId, props.recordProp.record, new BreakoutRoomBody(breakoutName,
externalMeetingId, breakoutMeetingId, sequence)))
}
def handleBreakoutRoomEnded(msg: BreakoutRoomEnded) {
@ -114,21 +124,21 @@ trait BreakoutRoomApp extends SystemConfiguration {
def handleBreakoutRoomUsersUpdate(msg: BreakoutRoomUsersUpdate) {
liveMeeting.breakoutModel.updateBreakoutUsers(msg.breakoutMeetingId, msg.users) foreach { room =>
outGW.send(new UpdateBreakoutUsersOutMessage(mProps.meetingID, mProps.recorded, msg.breakoutMeetingId, room.users))
outGW.send(new UpdateBreakoutUsersOutMessage(props.meetingProp.intId, props.recordProp.record, msg.breakoutMeetingId, room.users))
}
}
def handleSendBreakoutUsersUpdate(msg: SendBreakoutUsersUpdate) {
val users = Users.getUsers(liveMeeting.users)
val breakoutUsers = users map { u => new BreakoutUser(u.externalId, u.name) }
eventBus.publish(BigBlueButtonEvent(mProps.parentMeetingID,
new BreakoutRoomUsersUpdate(mProps.parentMeetingID, mProps.meetingID, breakoutUsers)))
eventBus.publish(BigBlueButtonEvent(props.breakoutProps.parentId,
new BreakoutRoomUsersUpdate(props.breakoutProps.parentId, props.meetingProp.intId, 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) {
if (!props.meetingProp.isBreakout) {
liveMeeting.breakoutModel.getBreakoutRoom(msg.targetMeetingId) match {
case Some(b) => {
targetVoiceBridge = b.voiceConfId;
@ -137,14 +147,14 @@ trait BreakoutRoomApp extends SystemConfiguration {
}
} // if it is a breakout room, the target voice bridge is the same after removing the last digit
else {
targetVoiceBridge = mProps.voiceBridge.dropRight(1)
targetVoiceBridge = props.voiceProp.voiceConf.dropRight(1)
}
// We check the user from the mode
Users.findWithId(msg.userId, liveMeeting.users) match {
case Some(u) => {
if (u.voiceUser.joined) {
log.info("Transferring user userId=" + u.id + " from voiceBridge=" + mProps.voiceBridge + " to targetVoiceConf=" + targetVoiceBridge)
outGW.send(new TransferUserToMeeting(mProps.voiceBridge, targetVoiceBridge, u.voiceUser.userId))
log.info("Transferring user userId=" + u.id + " from voiceBridge=" + props.voiceProp.voiceConf + " to targetVoiceConf=" + targetVoiceBridge)
outGW.send(new TransferUserToMeeting(props.voiceProp.voiceConf, targetVoiceBridge, u.voiceUser.userId))
}
}
case None => // do nothing
@ -152,7 +162,7 @@ trait BreakoutRoomApp extends SystemConfiguration {
}
def handleEndAllBreakoutRooms(msg: EndAllBreakoutRooms) {
log.info("EndAllBreakoutRooms event received for meetingId={}", mProps.meetingID)
log.info("EndAllBreakoutRooms event received for meetingId={}", props.meetingProp.intId)
liveMeeting.breakoutModel.getRooms().foreach { room =>
outGW.send(new EndBreakoutRoom(room.id))
}
@ -174,7 +184,7 @@ object BreakoutRoomsUtil {
}
def createJoinURL(webAPI: String, apiCall: String, baseString: String, checksum: String): String = {
var apiURL = if (webAPI.endsWith("/")) webAPI else webAPI.concat("/")
val apiURL = if (webAPI.endsWith("/")) webAPI else webAPI.concat("/")
apiURL.concat(apiCall).concat("?").concat(baseString).concat("&checksum=").concat(checksum)
}

View File

@ -1,8 +1,6 @@
package org.bigbluebutton.core.apps
import org.bigbluebutton.core.api._
import scala.collection.mutable.ArrayBuffer
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.running.{ MeetingActor }
@ -12,9 +10,9 @@ trait CaptionApp {
val outGW: OutMessageGateway
def handleSendCaptionHistoryRequest(msg: SendCaptionHistoryRequest) {
var history = liveMeeting.captionModel.getHistory()
val history = liveMeeting.captionModel.getHistory()
//println("Caption history requested " + history)
outGW.send(new SendCaptionHistoryReply(mProps.meetingID, mProps.recorded, msg.requesterID, history))
outGW.send(new SendCaptionHistoryReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, history))
}
def handleUpdateCaptionOwnerRequest(msg: UpdateCaptionOwnerRequest) {
@ -24,7 +22,7 @@ trait CaptionApp {
liveMeeting.captionModel.changeTranscriptOwner(t, "")
// send notification that owner has changed
outGW.send(new UpdateCaptionOwnerReply(mProps.meetingID, mProps.recorded, t, liveMeeting.captionModel.findLocaleCodeByLocale(t), ""))
outGW.send(new UpdateCaptionOwnerReply(props.meetingProp.intId, props.recordProp.record, t, liveMeeting.captionModel.findLocaleCodeByLocale(t), ""))
})
}
// create the locale if it doesn't exist
@ -34,7 +32,7 @@ trait CaptionApp {
liveMeeting.captionModel.newTranscript(msg.locale, msg.localeCode, msg.ownerID)
}
outGW.send(new UpdateCaptionOwnerReply(mProps.meetingID, mProps.recorded, msg.locale, msg.localeCode, msg.ownerID))
outGW.send(new UpdateCaptionOwnerReply(props.meetingProp.intId, props.recordProp.record, msg.locale, msg.localeCode, msg.ownerID))
}
def handleEditCaptionHistoryRequest(msg: EditCaptionHistoryRequest) {
@ -42,7 +40,7 @@ trait CaptionApp {
if (t == msg.locale) {
liveMeeting.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.localeCode, msg.text))
outGW.send(new EditCaptionHistoryReply(props.meetingProp.intId, props.recordProp.record, msg.userID, msg.startIndex, msg.endIndex, msg.locale, msg.localeCode, msg.text))
}
})
}
@ -52,7 +50,7 @@ trait CaptionApp {
liveMeeting.captionModel.changeTranscriptOwner(t, "")
// send notification that owner has changed
outGW.send(new UpdateCaptionOwnerReply(mProps.meetingID, mProps.recorded, t, liveMeeting.captionModel.findLocaleCodeByLocale(t), ""))
outGW.send(new UpdateCaptionOwnerReply(props.meetingProp.intId, props.recordProp.record, t, liveMeeting.captionModel.findLocaleCodeByLocale(t), ""))
})
}
}

View File

@ -1,8 +1,5 @@
package org.bigbluebutton.core.apps
import scala.collection.mutable.ArrayBuffer
import scala.collection.immutable.HashMap
class CaptionModel {
var transcripts = Map[String, Array[String]]()
@ -46,12 +43,12 @@ class CaptionModel {
//println("editHistory entered")
if (transcripts contains locale) {
//println("editHistory found locale:" + locale)
var oText: String = transcripts(locale)(1)
val 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)
val sText: String = oText.substring(0, startIndex)
val eText: String = oText.substring(endIndex)
transcripts(locale)(1) = (sText + text + eText)
//println("editHistory new history is: " + transcripts(locale)(1))

View File

@ -1,36 +1,36 @@
package org.bigbluebutton.core.apps
import org.bigbluebutton.common2.domain.DefaultProps
import org.bigbluebutton.core.api._
import scala.collection.mutable.ArrayBuffer
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor }
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.{ OutMessageGateway }
trait ChatApp {
this: MeetingActor =>
val props: DefaultProps
val liveMeeting: LiveMeeting
val outGW: OutMessageGateway
def handleGetChatHistoryRequest(msg: GetChatHistoryRequest) {
val history = liveMeeting.chatModel.getChatHistory()
outGW.send(new GetChatHistoryReply(mProps.meetingID, mProps.recorded, msg.requesterID, msg.replyTo, history))
outGW.send(new GetChatHistoryReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.replyTo, history))
}
def handleSendPublicMessageRequest(msg: SendPublicMessageRequest) {
liveMeeting.chatModel.addNewChatMessage(msg.message.toMap)
val pubMsg = msg.message.toMap
liveMeeting.chatModel.addNewChatMessage(msg.message)
val pubMsg = msg.message
outGW.send(new SendPublicMessageEvent(mProps.meetingID, mProps.recorded, msg.requesterID, pubMsg))
outGW.send(new SendPublicMessageEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, pubMsg))
}
def handleSendPrivateMessageRequest(msg: SendPrivateMessageRequest) {
val privMsg = msg.message.toMap
outGW.send(new SendPrivateMessageEvent(mProps.meetingID, mProps.recorded, msg.requesterID, privMsg))
val privMsg = msg.message
outGW.send(new SendPrivateMessageEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, privMsg))
}
def handleClearPublicChatHistoryRequest(msg: ClearPublicChatHistoryRequest) {
liveMeeting.chatModel.clearPublicChatHistory()
outGW.send(new ClearPublicChatHistoryReply(mProps.meetingID, mProps.recorded, msg.requesterID))
outGW.send(new ClearPublicChatHistoryReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID))
}
}

View File

@ -1,9 +1,15 @@
package org.bigbluebutton.core.apps
import org.bigbluebutton.core.models.{ DirectChats, PublicChats, UserIdAndName }
import org.bigbluebutton.core.models.{ DirectChats, PublicChats }
import scala.collection.mutable.ArrayBuffer
object ChatModel {
def getChatHistory(chatModel: ChatModel): Array[Map[String, String]] = {
chatModel.getChatHistory()
}
}
class ChatModel {
val directChats = new DirectChats

View File

@ -0,0 +1,19 @@
package org.bigbluebutton.core.apps
import org.bigbluebutton.core.running.LiveMeeting
trait ChatModelTrait {
this: LiveMeeting =>
def getChatHistory(): Array[Map[String, String]] = {
ChatModel.getChatHistory(chatModel)
}
def addNewChatMessage(msg: Map[String, String]) = {
chatModel.addNewChatMessage(msg)
}
def clearPublicChatHistory(): Unit = {
chatModel.clearPublicChatHistory()
}
}

View File

@ -5,7 +5,8 @@ import org.bigbluebutton.core.api._
import scala.collection.mutable.ArrayBuffer
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.models.{ Roles, UserVO, Users }
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor }
import org.bigbluebutton.core.running.MeetingActor
import org.bigbluebutton.core2.MeetingStatus2x
trait LayoutApp {
this: MeetingActor =>
@ -13,15 +14,17 @@ trait LayoutApp {
val outGW: OutMessageGateway
def handleGetCurrentLayoutRequest(msg: GetCurrentLayoutRequest) {
outGW.send(new GetCurrentLayoutReply(msg.meetingID, mProps.recorded, msg.requesterID,
liveMeeting.layoutModel.getCurrentLayout(), liveMeeting.meetingModel.getPermissions().lockedLayout, liveMeeting.layoutModel.getLayoutSetter()))
outGW.send(new GetCurrentLayoutReply(msg.meetingID, props.recordProp.record, msg.requesterID,
liveMeeting.layoutModel.getCurrentLayout(),
MeetingStatus2x.getPermissions(liveMeeting.status).lockedLayout,
liveMeeting.layoutModel.getLayoutSetter()))
}
def handleLockLayoutRequest(msg: LockLayoutRequest) {
liveMeeting.layoutModel.applyToViewersOnly(msg.viewersOnly)
liveMeeting.lockLayout(msg.lock)
outGW.send(new LockLayoutEvent(msg.meetingID, mProps.recorded, msg.setById, msg.lock, affectedUsers))
outGW.send(new LockLayoutEvent(msg.meetingID, props.recordProp.record, msg.setById, msg.lock, affectedUsers))
msg.layout foreach { l =>
liveMeeting.layoutModel.setCurrentLayout(l)
@ -30,8 +33,10 @@ trait LayoutApp {
}
private def broadcastSyncLayout(meetingId: String, setById: String) {
outGW.send(new BroadcastLayoutEvent(meetingId, mProps.recorded, setById,
liveMeeting.layoutModel.getCurrentLayout(), liveMeeting.meetingModel.getPermissions().lockedLayout, liveMeeting.layoutModel.getLayoutSetter(), affectedUsers))
outGW.send(new BroadcastLayoutEvent(meetingId, props.recordProp.record, setById,
liveMeeting.layoutModel.getCurrentLayout(),
MeetingStatus2x.getPermissions(liveMeeting.status).lockedLayout,
liveMeeting.layoutModel.getLayoutSetter(), affectedUsers))
}
def handleBroadcastLayoutRequest(msg: BroadcastLayoutRequest) {
@ -40,9 +45,9 @@ trait LayoutApp {
}
def handleLockLayout(lock: Boolean, setById: String) {
outGW.send(new LockLayoutEvent(mProps.meetingID, mProps.recorded, setById, lock, affectedUsers))
outGW.send(new LockLayoutEvent(props.meetingProp.intId, props.recordProp.record, setById, lock, affectedUsers))
broadcastSyncLayout(mProps.meetingID, setById)
broadcastSyncLayout(props.meetingProp.intId, setById)
}
def affectedUsers(): Array[UserVO] = {

View File

@ -1,6 +1,5 @@
package org.bigbluebutton.core.apps
import scala.collection.mutable.HashMap
import scala.collection.mutable.ArrayBuffer
object PollType {
@ -42,7 +41,7 @@ object PollFactory {
if (numQs > 0 && numQs <= 6) {
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, PollType.LetterPollType, multiResponse, None, answers)

View File

@ -28,13 +28,13 @@ trait PollApp {
poll match {
case Some(p) => {
if (p.started && p.stopped && p.showResult) {
outGW.send(new GetCurrentPollReplyMessage(mProps.meetingID, mProps.recorded, msg.requesterId, true, Some(p)))
outGW.send(new GetCurrentPollReplyMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, true, Some(p)))
} else {
outGW.send(new GetCurrentPollReplyMessage(mProps.meetingID, mProps.recorded, msg.requesterId, false, None))
outGW.send(new GetCurrentPollReplyMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, false, None))
}
}
case None => {
outGW.send(new GetCurrentPollReplyMessage(mProps.meetingID, mProps.recorded, msg.requesterId, false, None))
outGW.send(new GetCurrentPollReplyMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, false, None))
}
}
}
@ -50,7 +50,7 @@ trait PollApp {
poll <- PollModel.getPoll(msg.pollId, liveMeeting.pollModel)
} yield {
PollModel.hidePollResult(msg.pollId, liveMeeting.pollModel)
outGW.send(new PollHideResultMessage(mProps.meetingID, mProps.recorded, msg.requesterId, msg.pollId))
outGW.send(new PollHideResultMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, msg.pollId))
}
}
@ -93,7 +93,7 @@ trait PollApp {
page <- liveMeeting.presModel.getCurrentPage()
pageId = if (poll.id.contains("deskshare")) "deskshare" else page.id
annotation = new AnnotationVO(poll.id, WhiteboardKeyUtil.DRAW_END_STATUS, WhiteboardKeyUtil.POLL_RESULT_TYPE, shape, page.id, msg.requesterId, -1)
} handleSendWhiteboardAnnotationRequest(new SendWhiteboardAnnotationRequest(mProps.meetingID, msg.requesterId, annotation))
} handleSendWhiteboardAnnotationRequest(new SendWhiteboardAnnotationRequest(props.meetingProp.intId, msg.requesterId, annotation))
}
for {
@ -102,7 +102,7 @@ trait PollApp {
} yield {
send(poll, shape)
PollModel.showPollResult(msg.pollId, liveMeeting.pollModel)
outGW.send(new PollShowResultMessage(mProps.meetingID, mProps.recorded, msg.requesterId, msg.pollId, poll))
outGW.send(new PollShowResultMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, msg.pollId, poll))
}
}
@ -112,7 +112,7 @@ trait PollApp {
curPoll <- PollModel.getRunningPollThatStartsWith(page.id, liveMeeting.pollModel)
} yield {
PollModel.stopPoll(curPoll.id, liveMeeting.pollModel)
outGW.send(new PollStoppedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, curPoll.id))
outGW.send(new PollStoppedMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, curPoll.id))
}
}
@ -137,7 +137,7 @@ trait PollApp {
simplePoll <- PollModel.getSimplePoll(pollId, liveMeeting.pollModel)
} yield {
PollModel.startPoll(poll.id, liveMeeting.pollModel)
outGW.send(new PollStartedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, pollId, simplePoll))
outGW.send(new PollStartedMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, pollId, simplePoll))
}
}
@ -161,7 +161,7 @@ trait PollApp {
simplePoll <- PollModel.getSimplePoll(pollId, liveMeeting.pollModel)
} yield {
PollModel.startPoll(poll.id, liveMeeting.pollModel)
outGW.send(new PollStartedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, pollId, simplePoll))
outGW.send(new PollStartedMessage(props.meetingProp.intId, props.recordProp.record, msg.requesterId, pollId, simplePoll))
}
}
@ -188,7 +188,7 @@ trait PollApp {
responder = new Responder(user.id, user.name)
updatedPoll <- storePollResult(responder)
curPres <- Users.getCurrentPresenter(liveMeeting.users)
} yield outGW.send(new UserRespondedToPollMessage(mProps.meetingID, mProps.recorded, curPres.id, msg.pollId, updatedPoll))
} yield outGW.send(new UserRespondedToPollMessage(props.meetingProp.intId, props.recordProp.record, curPres.id, msg.pollId, updatedPoll))
}
}

View File

@ -1,10 +1,10 @@
package org.bigbluebutton.core.apps
import org.bigbluebutton.core.api._
import com.google.gson.Gson
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.models.Users
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor }
import org.bigbluebutton.core.running.MeetingActor
import org.bigbluebutton.core2.MeetingStatus2x
trait PresentationApp {
this: MeetingActor =>
@ -26,23 +26,23 @@ trait PresentationApp {
}
def handleClearPresentation(msg: ClearPresentation) {
outGW.send(new ClearPresentationOutMsg(mProps.meetingID, mProps.recorded))
outGW.send(new ClearPresentationOutMsg(props.meetingProp.intId, props.recordProp.record))
}
def handlePresentationConversionUpdate(msg: PresentationConversionUpdate) {
outGW.send(new PresentationConversionProgress(mProps.meetingID, msg.messageKey,
outGW.send(new PresentationConversionProgress(props.meetingProp.intId, msg.messageKey,
msg.code, msg.presentationId, msg.presName))
}
def handlePresentationPageCountError(msg: PresentationPageCountError) {
outGW.send(new PresentationConversionError(mProps.meetingID, msg.messageKey,
outGW.send(new PresentationConversionError(props.meetingProp.intId, msg.messageKey,
msg.code, msg.presentationId,
msg.numberOfPages,
msg.maxNumberPages, msg.presName))
}
def handlePresentationSlideGenerated(msg: PresentationSlideGenerated) {
outGW.send(new PresentationPageGenerated(mProps.meetingID, msg.messageKey,
outGW.send(new PresentationPageGenerated(props.meetingProp.intId, msg.messageKey,
msg.code, msg.presentationId,
msg.numberOfPages,
msg.pagesCompleted, msg.presName))
@ -52,7 +52,7 @@ trait PresentationApp {
liveMeeting.presModel.addPresentation(msg.presentation)
outGW.send(new PresentationConversionDone(mProps.meetingID, mProps.recorded, msg.messageKey,
outGW.send(new PresentationConversionDone(props.meetingProp.intId, props.recordProp.record, msg.messageKey,
msg.code, msg.presentation))
sharePresentation(msg.presentation.id, true)
@ -69,16 +69,16 @@ trait PresentationApp {
}
})
outGW.send(new RemovePresentationOutMsg(msg.meetingID, mProps.recorded, msg.presentationID))
outGW.send(new RemovePresentationOutMsg(msg.meetingID, props.recordProp.record, msg.presentationID))
}
def handleGetPresentationInfo(msg: GetPresentationInfo) {
val curPresenter = liveMeeting.getCurrentPresenterInfo()
val curPresenter = MeetingStatus2x.getCurrentPresenterInfo(liveMeeting.status)
val presenter = new CurrentPresenter(curPresenter.presenterID, curPresenter.presenterName, curPresenter.assignedBy)
val presentations = liveMeeting.presModel.getPresentations
val presentationInfo = new CurrentPresentationInfo(presenter, presentations)
outGW.send(new GetPresentationInfoOutMsg(mProps.meetingID, mProps.recorded, msg.requesterID, presentationInfo, msg.replyTo))
outGW.send(new GetPresentationInfoOutMsg(props.meetingProp.intId, props.recordProp.record, msg.requesterID, presentationInfo, msg.replyTo))
}
def handleResizeAndMoveSlide(msg: ResizeAndMoveSlide) {
@ -89,17 +89,17 @@ trait PresentationApp {
val height = if (msg.heightRatio <= 100) msg.heightRatio else 100
val page = liveMeeting.presModel.resizePage(xOffset, yOffset, width, height);
page foreach (p => outGW.send(new ResizeAndMoveSlideOutMsg(mProps.meetingID, mProps.recorded, p)))
page foreach (p => outGW.send(new ResizeAndMoveSlideOutMsg(props.meetingProp.intId, props.recordProp.record, p)))
}
def handleGotoSlide(msg: GotoSlide) {
liveMeeting.presModel.changePage(msg.page) foreach { page =>
log.debug("Switching page for meeting=[{}] page=[{}]", msg.meetingID, page.num);
outGW.send(new GotoSlideOutMsg(mProps.meetingID, mProps.recorded, page))
outGW.send(new GotoSlideOutMsg(props.meetingProp.intId, props.recordProp.record, page))
}
Users.getCurrentPresenter(liveMeeting.users) foreach { pres =>
handleStopPollRequest(StopPollRequest(mProps.meetingID, pres.id))
handleStopPollRequest(StopPollRequest(props.meetingProp.intId, pres.id))
}
}
@ -112,10 +112,10 @@ trait PresentationApp {
val pres = liveMeeting.presModel.sharePresentation(presentationID)
pres foreach { p =>
outGW.send(new SharePresentationOutMsg(mProps.meetingID, mProps.recorded, p))
outGW.send(new SharePresentationOutMsg(props.meetingProp.intId, props.recordProp.record, p))
liveMeeting.presModel.getCurrentPage(p) foreach { page =>
outGW.send(new GotoSlideOutMsg(mProps.meetingID, mProps.recorded, page))
outGW.send(new GotoSlideOutMsg(props.meetingProp.intId, props.recordProp.record, page))
}
}
@ -124,7 +124,7 @@ trait PresentationApp {
def handleGetSlideInfo(msg: GetSlideInfo) {
liveMeeting.presModel.getCurrentPresentation foreach { pres =>
liveMeeting.presModel.getCurrentPage(pres) foreach { page =>
outGW.send(new GetSlideInfoOutMsg(mProps.meetingID, mProps.recorded, msg.requesterID, page, msg.replyTo))
outGW.send(new GetSlideInfoOutMsg(props.meetingProp.intId, props.recordProp.record, msg.requesterID, page, msg.replyTo))
}
}

View File

@ -2,7 +2,7 @@ package org.bigbluebutton.core.apps
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor }
import org.bigbluebutton.core.running.{ MeetingActor }
trait SharedNotesApp {
this: MeetingActor =>
@ -19,20 +19,20 @@ trait SharedNotesApp {
val (patchID, patch, undo, redo) = liveMeeting.notesModel.patchDocument(msg.noteID, msg.patch, msg.operation)
if (patch != "") outGW.send(new PatchDocumentReply(mProps.meetingID, mProps.recorded, requesterID, msg.noteID, patch, patchID, undo, redo))
if (patch != "") outGW.send(new PatchDocumentReply(props.meetingProp.intId, props.recordProp.record, requesterID, msg.noteID, patch, patchID, undo, redo))
}
def handleGetCurrentDocumentRequest(msg: GetCurrentDocumentRequest) {
val notesReport = liveMeeting.notesModel.notesReport.toMap
outGW.send(new GetCurrentDocumentReply(mProps.meetingID, mProps.recorded, msg.requesterID, notesReport))
outGW.send(new GetCurrentDocumentReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, notesReport))
}
private def createAdditionalNotes(requesterID: String, noteName: String = "") {
liveMeeting.notesModel.synchronized {
val noteID = liveMeeting.notesModel.createNote(noteName)
outGW.send(new CreateAdditionalNotesReply(mProps.meetingID, mProps.recorded, requesterID, noteID, noteName))
outGW.send(new CreateAdditionalNotesReply(props.meetingProp.intId, props.recordProp.record, requesterID, noteID, noteName))
}
}
@ -44,7 +44,7 @@ trait SharedNotesApp {
liveMeeting.notesModel.synchronized {
liveMeeting.notesModel.destroyNote(msg.noteID)
outGW.send(new DestroyAdditionalNotesReply(mProps.meetingID, mProps.recorded, msg.requesterID, msg.noteID))
outGW.send(new DestroyAdditionalNotesReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.noteID))
}
}
@ -56,7 +56,7 @@ trait SharedNotesApp {
def handleSharedNotesSyncNoteRequest(msg: SharedNotesSyncNoteRequest) {
liveMeeting.notesModel.getNoteReport(msg.noteID) match {
case Some(note) => outGW.send(new SharedNotesSyncNoteReply(mProps.meetingID, mProps.recorded, msg.requesterID, msg.noteID, note))
case Some(note) => outGW.send(new SharedNotesSyncNoteReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.noteID, note))
case None =>
}
}

View File

@ -3,11 +3,9 @@ package org.bigbluebutton.core.apps
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.api.SharedNotesOperation._
import name.fraser.neil.plaintext.diff_match_patch
import name.fraser.neil.plaintext.diff_match_patch._
import scala.collection.mutable.Stack
import scala.collection.mutable.HashMap
import scala.collection._
import java.util.Collections
class SharedNotesModel {
val notes = new HashMap[String, Note]()

View File

@ -1,11 +1,11 @@
package org.bigbluebutton.core.apps
import org.bigbluebutton.core.api._
import scala.collection.immutable.ListSet
import org.bigbluebutton.core.OutMessageGateway
import org.bigbluebutton.core.api.GuestPolicy
import org.bigbluebutton.core.models._
import org.bigbluebutton.core.running.MeetingActor
import org.bigbluebutton.core2.MeetingStatus2x
trait UsersApp {
this: MeetingActor =>
@ -13,40 +13,40 @@ trait UsersApp {
val outGW: OutMessageGateway
def handleUserConnectedToGlobalAudio(msg: UserConnectedToGlobalAudio) {
log.info("Handling UserConnectedToGlobalAudio: meetingId=" + mProps.meetingID + " userId=" + msg.userid)
log.info("Handling UserConnectedToGlobalAudio: meetingId=" + props.meetingProp.intId + " userId=" + msg.userid)
val user = Users.findWithId(msg.userid, liveMeeting.users)
user foreach { u =>
if (liveMeeting.addGlobalAudioConnection(msg.userid)) {
if (MeetingStatus2x.addGlobalAudioConnection(liveMeeting.status, msg.userid)) {
for {
uvo <- Users.joinedVoiceListenOnly(msg.userid, liveMeeting.users)
} yield {
log.info("UserConnectedToGlobalAudio: meetingId=" + mProps.meetingID + " userId=" + uvo.id + " user=" + uvo)
outGW.send(new UserListeningOnly(mProps.meetingID, mProps.recorded, uvo.id, uvo.listenOnly))
log.info("UserConnectedToGlobalAudio: meetingId=" + props.meetingProp.intId + " userId=" + uvo.id + " user=" + uvo)
outGW.send(new UserListeningOnly(props.meetingProp.intId, props.recordProp.record, uvo.id, uvo.listenOnly))
}
}
}
}
def handleUserDisconnectedFromGlobalAudio(msg: UserDisconnectedFromGlobalAudio) {
log.info("Handling UserDisconnectedToGlobalAudio: meetingId=" + mProps.meetingID + " userId=" + msg.userid)
log.info("Handling UserDisconnectedToGlobalAudio: meetingId=" + props.meetingProp.intId + " userId=" + msg.userid)
val user = Users.findWithId(msg.userid, liveMeeting.users)
user foreach { u =>
if (liveMeeting.removeGlobalAudioConnection(msg.userid)) {
if (MeetingStatus2x.removeGlobalAudioConnection(liveMeeting.status, msg.userid)) {
if (!u.joinedWeb) {
for {
uvo <- Users.userLeft(u.id, liveMeeting.users)
} yield {
log.info("Not web user. Send user left message. meetingId=" + mProps.meetingID + " userId=" + u.id + " user=" + u)
outGW.send(new UserLeft(mProps.meetingID, mProps.recorded, uvo))
log.info("Not web user. Send user left message. meetingId=" + props.meetingProp.intId + " userId=" + u.id + " user=" + u)
outGW.send(new UserLeft(props.meetingProp.intId, props.recordProp.record, uvo))
}
} else {
for {
uvo <- Users.leftVoiceListenOnly(u.id, liveMeeting.users)
} yield {
log.info("UserDisconnectedToGlobalAudio: meetingId=" + mProps.meetingID + " userId=" + uvo.id + " user=" + uvo)
outGW.send(new UserListeningOnly(mProps.meetingID, mProps.recorded, uvo.id, uvo.listenOnly))
log.info("UserDisconnectedToGlobalAudio: meetingId=" + props.meetingProp.intId + " userId=" + uvo.id + " user=" + uvo)
outGW.send(new UserListeningOnly(props.meetingProp.intId, props.recordProp.record, uvo.id, uvo.listenOnly))
}
}
}
@ -55,28 +55,30 @@ trait UsersApp {
def handleMuteAllExceptPresenterRequest(msg: MuteAllExceptPresenterRequest) {
if (msg.mute) {
liveMeeting.meetingModel.muteMeeting()
MeetingStatus2x.muteMeeting(liveMeeting.status)
} else {
liveMeeting.meetingModel.unmuteMeeting()
MeetingStatus2x.unmuteMeeting(liveMeeting.status)
}
outGW.send(new MeetingMuted(mProps.meetingID, mProps.recorded, liveMeeting.meetingModel.isMeetingMuted()))
outGW.send(new MeetingMuted(props.meetingProp.intId, props.recordProp.record,
MeetingStatus2x.isMeetingMuted(liveMeeting.status)))
usersWhoAreNotPresenter foreach { u =>
outGW.send(new MuteVoiceUser(mProps.meetingID, mProps.recorded, msg.requesterID,
u.id, mProps.voiceBridge, u.voiceUser.userId, msg.mute))
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record, msg.requesterID,
u.id, props.voiceProp.voiceConf, u.voiceUser.userId, msg.mute))
}
}
def handleMuteMeetingRequest(msg: MuteMeetingRequest) {
if (msg.mute) {
liveMeeting.meetingModel.muteMeeting()
MeetingStatus2x.muteMeeting(liveMeeting.status)
} else {
liveMeeting.meetingModel.unmuteMeeting()
MeetingStatus2x.unmuteMeeting(liveMeeting.status)
}
outGW.send(new MeetingMuted(mProps.meetingID, mProps.recorded, liveMeeting.meetingModel.isMeetingMuted()))
outGW.send(new MeetingMuted(props.meetingProp.intId, props.recordProp.record,
MeetingStatus2x.isMeetingMuted(liveMeeting.status)))
Users.getUsers(liveMeeting.users) foreach { u =>
outGW.send(new MuteVoiceUser(mProps.meetingID, mProps.recorded, msg.requesterID,
u.id, mProps.voiceBridge, u.voiceUser.userId, msg.mute))
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record, msg.requesterID,
u.id, props.voiceProp.voiceConf, u.voiceUser.userId, msg.mute))
}
}
@ -84,49 +86,48 @@ trait UsersApp {
log.info("Got ValidateAuthToken message. meetingId=" + msg.meetingID + " userId=" + msg.userId)
RegisteredUsers.getRegisteredUserWithToken(msg.token, msg.userId, liveMeeting.registeredUsers) match {
case Some(u) =>
val replyTo = mProps.meetingID + '/' + msg.userId
//send the reply
outGW.send(new ValidateAuthTokenReply(mProps.meetingID, msg.userId, msg.token, true, msg.correlationId))
outGW.send(new ValidateAuthTokenReply(props.meetingProp.intId, msg.userId, msg.token, true, msg.correlationId))
log.info("ValidateToken success. meetingId=" + mProps.meetingID + " userId=" + msg.userId)
log.info("ValidateToken success. meetingId=" + props.meetingProp.intId + " userId=" + msg.userId)
//join the user
handleUserJoin(new UserJoining(mProps.meetingID, msg.userId, msg.token))
handleUserJoin(new UserJoining(props.meetingProp.intId, msg.userId, msg.token))
case None =>
log.info("ValidateToken failed. meetingId=" + mProps.meetingID + " userId=" + msg.userId)
outGW.send(new ValidateAuthTokenReply(mProps.meetingID, msg.userId, msg.token, false, msg.correlationId))
log.info("ValidateToken failed. meetingId=" + props.meetingProp.intId + " userId=" + msg.userId)
outGW.send(new ValidateAuthTokenReply(props.meetingProp.intId, msg.userId, msg.token, false, msg.correlationId))
}
}
def handleRegisterUser(msg: RegisterUser) {
if (liveMeeting.meetingModel.hasMeetingEnded()) {
if (MeetingStatus2x.hasMeetingEnded(liveMeeting.status)) {
// Check first if the meeting has ended and the user refreshed the client to re-connect.
log.info("Register user failed. Mmeeting has ended. meetingId=" + mProps.meetingID + " userId=" + msg.userID)
log.info("Register user failed. Mmeeting has ended. meetingId=" + props.meetingProp.intId + " userId=" + msg.userID)
sendMeetingHasEnded(msg.userID)
} else {
val regUser = RegisteredUsers.create(msg.userID, msg.extUserID, msg.name, msg.role, msg.authToken,
msg.avatarURL, msg.guest, msg.authed, msg.guest, liveMeeting.registeredUsers)
log.info("Register user success. meetingId=" + mProps.meetingID + " userId=" + msg.userID + " user=" + regUser)
outGW.send(new UserRegistered(mProps.meetingID, mProps.recorded, regUser))
log.info("Register user success. meetingId=" + props.meetingProp.intId + " userId=" + msg.userID + " user=" + regUser)
outGW.send(new UserRegistered(props.meetingProp.intId, props.recordProp.record, regUser))
}
}
def handleIsMeetingMutedRequest(msg: IsMeetingMutedRequest) {
outGW.send(new IsMeetingMutedReply(mProps.meetingID, mProps.recorded,
msg.requesterID, liveMeeting.meetingModel.isMeetingMuted()))
outGW.send(new IsMeetingMutedReply(props.meetingProp.intId, props.recordProp.record,
msg.requesterID, MeetingStatus2x.isMeetingMuted(liveMeeting.status)))
}
def handleMuteUserRequest(msg: MuteUserRequest) {
log.info("Received mute user request. meetingId=" + mProps.meetingID + " userId=" + msg.userID + " mute=" + msg.mute)
log.info("Received mute user request. meetingId=" + props.meetingProp.intId + " userId=" + msg.userID + " mute=" + msg.mute)
for {
u <- Users.findWithId(msg.userID, liveMeeting.users)
} yield {
log.info("Send mute user request. meetingId=" + mProps.meetingID + " userId=" + u.id + " user=" + u)
outGW.send(new MuteVoiceUser(mProps.meetingID, mProps.recorded,
msg.requesterID, u.id, mProps.voiceBridge, u.voiceUser.userId, msg.mute))
log.info("Send mute user request. meetingId=" + props.meetingProp.intId + " userId=" + u.id + " user=" + u)
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record,
msg.requesterID, u.id, props.voiceProp.voiceConf, u.voiceUser.userId, msg.mute))
}
}
@ -137,8 +138,9 @@ trait UsersApp {
u <- Users.findWithId(msg.userId, liveMeeting.users)
} yield {
if (u.voiceUser.joined) {
log.info("Ejecting user from voice. meetingId=" + mProps.meetingID + " userId=" + u.id)
outGW.send(new EjectVoiceUser(mProps.meetingID, mProps.recorded, msg.ejectedBy, u.id, mProps.voiceBridge, u.voiceUser.userId))
log.info("Ejecting user from voice. meetingId=" + props.meetingProp.intId + " userId=" + u.id)
outGW.send(new EjectVoiceUser(props.meetingProp.intId, props.recordProp.record, msg.ejectedBy, u.id,
props.voiceProp.voiceConf, u.voiceUser.userId))
}
}
}
@ -147,15 +149,17 @@ trait UsersApp {
//println("*************** Reply with current lock settings ********************")
//reusing the existing handle for NewPermissionsSettings to reply to the GetLockSettings request
outGW.send(new NewPermissionsSetting(mProps.meetingID, msg.userId,
liveMeeting.meetingModel.getPermissions(), Users.getUsers(liveMeeting.users).toArray))
outGW.send(new NewPermissionsSetting(props.meetingProp.intId, msg.userId,
MeetingStatus2x.getPermissions(liveMeeting.status),
Users.getUsers(liveMeeting.users).toArray))
}
def handleSetLockSettings(msg: SetLockSettings) {
if (!liveMeeting.permissionsEqual(msg.settings)) {
liveMeeting.newPermissions(msg.settings)
outGW.send(new NewPermissionsSetting(mProps.meetingID, msg.setByUser,
liveMeeting.meetingModel.getPermissions, Users.getUsers(liveMeeting.users).toArray))
outGW.send(new NewPermissionsSetting(props.meetingProp.intId, msg.setByUser,
MeetingStatus2x.getPermissions(liveMeeting.status),
Users.getUsers(liveMeeting.users).toArray))
handleLockLayout(msg.settings.lockedLayout, msg.setByUser)
}
@ -165,26 +169,27 @@ trait UsersApp {
for {
uvo <- Users.lockUser(msg.userID, msg.lock, liveMeeting.users)
} yield {
log.info("Lock user. meetingId=" + mProps.meetingID + " userId=" + uvo.id + " locked=" + uvo.locked)
outGW.send(new UserLocked(mProps.meetingID, uvo.id, uvo.locked))
log.info("Lock user. meetingId=" + props.meetingProp.intId + " userId=" + uvo.id + " locked=" + uvo.locked)
outGW.send(new UserLocked(props.meetingProp.intId, uvo.id, uvo.locked))
}
}
def handleInitLockSettings(msg: InitLockSettings) {
if (!liveMeeting.meetingModel.permisionsInitialized()) {
liveMeeting.meetingModel.initializePermissions()
if (!MeetingStatus2x.permisionsInitialized(liveMeeting.status)) {
MeetingStatus2x.initializePermissions(liveMeeting.status)
liveMeeting.newPermissions(msg.settings)
outGW.send(new PermissionsSettingInitialized(msg.meetingID, msg.settings, Users.getUsers(liveMeeting.users).toArray))
outGW.send(new PermissionsSettingInitialized(msg.meetingID, msg.settings,
Users.getUsers(liveMeeting.users).toArray))
}
}
def handleInitAudioSettings(msg: InitAudioSettings) {
if (!liveMeeting.meetingModel.audioSettingsInitialized()) {
liveMeeting.meetingModel.initializeAudioSettings()
if (!MeetingStatus2x.audioSettingsInitialized(liveMeeting.status)) {
MeetingStatus2x.initializeAudioSettings(liveMeeting.status)
if (liveMeeting.meetingModel.isMeetingMuted() != msg.muted) {
if (MeetingStatus2x.isMeetingMuted(liveMeeting.status) != msg.muted) {
handleMuteAllExceptPresenterRequest(
new MuteAllExceptPresenterRequest(mProps.meetingID,
new MuteAllExceptPresenterRequest(props.meetingProp.intId,
msg.requesterID, msg.muted));
}
}
@ -198,7 +203,7 @@ trait UsersApp {
for {
uvo <- Users.setEmojiStatus(msg.userId, liveMeeting.users, msg.emojiStatus)
} yield {
outGW.send(new UserChangedEmojiStatus(mProps.meetingID, mProps.recorded, msg.emojiStatus, uvo.id))
outGW.send(new UserChangedEmojiStatus(props.meetingProp.intId, props.recordProp.record, msg.emojiStatus, uvo.id))
}
}
@ -208,7 +213,7 @@ trait UsersApp {
} yield {
RegisteredUsers.updateRegUser(uvo, liveMeeting.registeredUsers)
val userRole = if (msg.role == Roles.MODERATOR_ROLE) "MODERATOR" else "VIEWER"
outGW.send(new UserRoleChange(mProps.meetingID, mProps.recorded, msg.userID, userRole))
outGW.send(new UserRoleChange(props.meetingProp.intId, props.recordProp.record, msg.userID, userRole))
}
}
@ -220,21 +225,23 @@ trait UsersApp {
*/
val moderator = Users.findAModerator(liveMeeting.users)
moderator.foreach { mod =>
log.info("Presenter left meeting. meetingId=" + mProps.meetingID + " userId=" + user.id
log.info("Presenter left meeting. meetingId=" + props.meetingProp.intId + " userId=" + user.id
+ ". Making user=[" + mod.id + "] presenter.")
assignNewPresenter(mod.id, mod.name, mod.id)
}
if (liveMeeting.meetingModel.isBroadcastingRTMP()) {
if (MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status)) {
// The presenter left during desktop sharing. Stop desktop sharing on FreeSWITCH
outGW.send(new DeskShareHangUp(mProps.meetingID, mProps.voiceBridge))
outGW.send(new DeskShareHangUp(props.meetingProp.intId, props.voiceProp.voiceConf))
// notify other clients to close their deskshare view
outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, liveMeeting.meetingModel.getRTMPBroadcastingUrl(),
liveMeeting.meetingModel.getDesktopShareVideoWidth(), liveMeeting.meetingModel.getDesktopShareVideoHeight(), false))
outGW.send(new DeskShareNotifyViewersRTMP(props.meetingProp.intId,
MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status),
MeetingStatus2x.getDesktopShareVideoWidth(liveMeeting.status),
MeetingStatus2x.getDesktopShareVideoHeight(liveMeeting.status), false))
// reset meeting info
liveMeeting.meetingModel.resetDesktopSharingParams()
MeetingStatus2x.resetDesktopSharingParams(liveMeeting.status)
}
}
}
@ -244,17 +251,17 @@ trait UsersApp {
user <- Users.userLeft(msg.userId, liveMeeting.users)
} yield {
if (user.voiceUser.joined) {
outGW.send(new EjectVoiceUser(mProps.meetingID, mProps.recorded,
msg.ejectedBy, msg.userId, mProps.voiceBridge, user.voiceUser.userId))
outGW.send(new EjectVoiceUser(props.meetingProp.intId, props.recordProp.record,
msg.ejectedBy, msg.userId, props.voiceProp.voiceConf, user.voiceUser.userId))
}
RegisteredUsers.remove(msg.userId, liveMeeting.registeredUsers)
makeSurePresenterIsAssigned(user)
log.info("Ejecting user from meeting. meetingId=" + mProps.meetingID + " userId=" + msg.userId)
outGW.send(new UserEjectedFromMeeting(mProps.meetingID, mProps.recorded, msg.userId, msg.ejectedBy))
outGW.send(new DisconnectUser(mProps.meetingID, msg.userId))
log.info("Ejecting user from meeting. meetingId=" + props.meetingProp.intId + " userId=" + msg.userId)
outGW.send(new UserEjectedFromMeeting(props.meetingProp.intId, props.recordProp.record, msg.userId, msg.ejectedBy))
outGW.send(new DisconnectUser(props.meetingProp.intId, msg.userId))
outGW.send(new UserLeft(msg.meetingID, mProps.recorded, user))
outGW.send(new UserLeft(msg.meetingID, props.recordProp.record, user))
}
}
@ -262,8 +269,8 @@ trait UsersApp {
for {
uvo <- Users.userSharedWebcam(msg.userId, liveMeeting.users, msg.stream)
} yield {
log.info("User shared webcam. meetingId=" + mProps.meetingID + " userId=" + uvo.id + " stream=" + msg.stream)
outGW.send(new UserSharedWebcam(mProps.meetingID, mProps.recorded, uvo.id, msg.stream))
log.info("User shared webcam. meetingId=" + props.meetingProp.intId + " userId=" + uvo.id + " stream=" + msg.stream)
outGW.send(new UserSharedWebcam(props.meetingProp.intId, props.recordProp.record, uvo.id, msg.stream))
}
}
@ -271,14 +278,14 @@ trait UsersApp {
for {
uvo <- Users.userUnsharedWebcam(msg.userId, liveMeeting.users, msg.stream)
} yield {
log.info("User unshared webcam. meetingId=" + mProps.meetingID + " userId=" + uvo.id + " stream=" + msg.stream)
outGW.send(new UserUnsharedWebcam(mProps.meetingID, mProps.recorded, uvo.id, msg.stream))
log.info("User unshared webcam. meetingId=" + props.meetingProp.intId + " userId=" + uvo.id + " stream=" + msg.stream)
outGW.send(new UserUnsharedWebcam(props.meetingProp.intId, props.recordProp.record, uvo.id, msg.stream))
}
}
def handleChangeUserStatus(msg: ChangeUserStatus): Unit = {
if (Users.hasUserWithId(msg.userID, liveMeeting.users)) {
outGW.send(new UserStatusChange(mProps.meetingID, mProps.recorded, msg.userID, msg.status, msg.value))
outGW.send(new UserStatusChange(props.meetingProp.intId, props.recordProp.record, msg.userID, msg.status, msg.value))
}
}
@ -287,14 +294,14 @@ trait UsersApp {
}
def handleUserJoin(msg: UserJoining): Unit = {
log.debug("Received user joined meeting. metingId=" + mProps.meetingID + " userId=" + msg.userID)
log.debug("Received user joined meeting. metingId=" + props.meetingProp.intId + " userId=" + msg.userID)
def initVoiceUser(userId: String, ru: RegisteredUser): VoiceUser = {
val wUser = Users.findWithId(userId, liveMeeting.users)
wUser match {
case Some(u) => {
log.debug("Found user. metingId=" + mProps.meetingID + " userId=" + msg.userID + " user=" + u)
log.debug("Found user. metingId=" + props.meetingProp.intId + " userId=" + msg.userID + " user=" + u)
if (u.voiceUser.joined) {
/*
* User is in voice conference. Must mean that the user reconnected with audio
@ -312,7 +319,7 @@ trait UsersApp {
}
}
case None => {
log.debug("User not found. metingId=" + mProps.meetingID + " userId=" + msg.userID)
log.debug("User not found. metingId=" + props.meetingProp.intId + " userId=" + msg.userID)
/**
* New user. Initialize voice status.
*/
@ -325,7 +332,7 @@ trait UsersApp {
val regUser = RegisteredUsers.getRegisteredUserWithToken(msg.authToken, msg.userID, liveMeeting.registeredUsers)
regUser foreach { ru =>
log.debug("Found registered user. metingId=" + mProps.meetingID + " userId=" + msg.userID + " ru=" + ru)
log.debug("Found registered user. metingId=" + props.meetingProp.intId + " userId=" + msg.userID + " ru=" + ru)
val wUser = Users.findWithId(msg.userID, liveMeeting.users)
@ -333,13 +340,13 @@ trait UsersApp {
wUser.foreach { w =>
if (!w.joinedWeb) {
log.debug("User is in voice only. Mark as user left. metingId=" + mProps.meetingID + " userId=" + msg.userID)
log.debug("User is in voice only. Mark as user left. metingId=" + props.meetingProp.intId + " userId=" + msg.userID)
/**
* If user is not joined through the web (perhaps reconnecting).
* Send a user left event to clear up user list of all clients.
*/
val user = Users.userLeft(w.id, liveMeeting.users)
outGW.send(new UserLeft(msg.meetingID, mProps.recorded, w))
Users.userLeft(w.id, liveMeeting.users)
outGW.send(new UserLeft(msg.meetingID, props.recordProp.record, w))
}
}
@ -347,29 +354,32 @@ trait UsersApp {
* Initialize the newly joined user copying voice status in case this
* join is due to a reconnect.
*/
val waitingForAcceptance = ru.guest && liveMeeting.meetingModel.getGuestPolicy() == GuestPolicy.ASK_MODERATOR && ru.waitingForAcceptance
val waitingForAcceptance = ru.guest &&
MeetingStatus2x.getGuestPolicy(liveMeeting.status) == GuestPolicy.ASK_MODERATOR && ru.waitingForAcceptance
val lockStatus = getInitialLockStatus(ru.role)
for {
uvo <- Users.newUser(msg.userID, lockStatus, ru, waitingForAcceptance, vu, liveMeeting.users)
} yield {
log.info("User joined meeting. metingId=" + mProps.meetingID + " userId=" + uvo.id + " user=" + uvo)
log.info("User joined meeting. metingId=" + props.meetingProp.intId + " userId=" + uvo.id + " user=" + uvo)
if (uvo.guest && liveMeeting.meetingModel.getGuestPolicy() == GuestPolicy.ALWAYS_DENY) {
outGW.send(new GuestAccessDenied(mProps.meetingID, mProps.recorded, uvo.id))
if (uvo.guest && MeetingStatus2x.getGuestPolicy(liveMeeting.status) == GuestPolicy.ALWAYS_DENY) {
outGW.send(new GuestAccessDenied(props.meetingProp.intId, props.recordProp.record, uvo.id))
} else {
outGW.send(new UserJoined(mProps.meetingID, mProps.recorded, uvo))
outGW.send(new MeetingState(mProps.meetingID, mProps.recorded, uvo.id, liveMeeting.meetingModel.getPermissions(), liveMeeting.meetingModel.isMeetingMuted()))
outGW.send(new UserJoined(props.meetingProp.intId, props.recordProp.record, uvo))
outGW.send(new MeetingState(props.meetingProp.intId, props.recordProp.record, uvo.id,
MeetingStatus2x.getPermissions(liveMeeting.status), MeetingStatus2x.isMeetingMuted(liveMeeting.status)))
if (!waitingForAcceptance) {
// Become presenter if the only moderator
if ((Users.numModerators(liveMeeting.users) == 1) || (Users.hasNoPresenter(liveMeeting.users))) {
if (ru.role == Roles.MODERATOR_ROLE) {
log.info("Assigning presenter to lone moderator. metingId=" + mProps.meetingID + " userId=" + uvo.id)
log.info("Assigning presenter to lone moderator. metingId=" + props.meetingProp.intId + " userId=" + uvo.id)
assignNewPresenter(msg.userID, ru.name, msg.userID)
}
}
} else {
log.info("User waiting for acceptance. metingId=" + mProps.meetingID + " userId=" + uvo.id)
log.info("User waiting for acceptance. metingId=" + props.meetingProp.intId + " userId=" + uvo.id)
}
liveMeeting.webUserJoined
startRecordingIfAutoStart()
@ -382,8 +392,8 @@ trait UsersApp {
for {
u <- Users.userLeft(msg.userID, liveMeeting.users)
} yield {
log.info("User left meeting. meetingId=" + mProps.meetingID + " userId=" + u.id + " user=" + u)
outGW.send(new UserLeft(msg.meetingID, mProps.recorded, u))
log.info("User left meeting. meetingId=" + props.meetingProp.intId + " userId=" + u.id + " user=" + u)
outGW.send(new UserLeft(msg.meetingID, props.recordProp.record, u))
makeSurePresenterIsAssigned(u)
@ -394,7 +404,7 @@ trait UsersApp {
* and is reconnecting. Make the user as joined only in the voice conference. If we get a
* user left voice conference message, then we will remove the user from the users list.
*/
switchUserToPhoneUser(new UserJoinedVoiceConfMessage(mProps.voiceBridge,
switchUserToPhoneUser(new UserJoinedVoiceConfMessage(props.voiceProp.voiceConf,
vu.userId, u.id, u.externalId, vu.callerName,
vu.callerNum, vu.muted, vu.talking, vu.avatarURL, u.listenOnly));
}
@ -406,16 +416,16 @@ trait UsersApp {
}
def getInitialLockStatus(role: String): Boolean = {
liveMeeting.meetingModel.getPermissions().lockOnJoin && !role.equals(Roles.MODERATOR_ROLE)
MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin && !role.equals(Roles.MODERATOR_ROLE)
}
def handleUserJoinedVoiceFromPhone(msg: UserJoinedVoiceConfMessage) = {
log.info("User joining from phone. meetingId=" + mProps.meetingID + " userId=" + msg.userId + " extUserId=" + msg.externUserId)
log.info("User joining from phone. meetingId=" + props.meetingProp.intId + " userId=" + msg.userId + " extUserId=" + msg.externUserId)
val user = Users.getUserWithVoiceUserId(msg.voiceUserId, liveMeeting.users) match {
Users.getUserWithVoiceUserId(msg.voiceUserId, liveMeeting.users) match {
case Some(user) => {
log.info("Voice user=" + msg.voiceUserId + " is already in conf="
+ mProps.voiceBridge + ". Must be duplicate message. meetigId=" + mProps.meetingID)
+ props.voiceProp.voiceConf + ". Must be duplicate message. meetigId=" + props.meetingProp.intId)
}
case None => {
val webUserId = if (msg.userId != msg.callerIdName) {
@ -441,14 +451,14 @@ trait UsersApp {
lockStatus = getInitialLockStatus(Roles.VIEWER_ROLE),
listenOnly = msg.listenOnly, avatarURL = msg.avatarURL)
log.info("User joined from phone. meetingId=" + mProps.meetingID + " userId=" + uvo.id + " user=" + uvo)
log.info("User joined from phone. meetingId=" + props.meetingProp.intId + " userId=" + uvo.id + " user=" + uvo)
outGW.send(new UserJoined(mProps.meetingID, mProps.recorded, uvo))
outGW.send(new UserJoinedVoice(mProps.meetingID, mProps.recorded, mProps.voiceBridge, uvo))
outGW.send(new UserJoined(props.meetingProp.intId, props.recordProp.record, uvo))
outGW.send(new UserJoinedVoice(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, uvo))
if (liveMeeting.meetingModel.isMeetingMuted()) {
outGW.send(new MuteVoiceUser(mProps.meetingID, mProps.recorded, uvo.id, uvo.id,
mProps.voiceBridge, vu.userId, liveMeeting.meetingModel.isMeetingMuted()))
if (MeetingStatus2x.isMeetingMuted(liveMeeting.status)) {
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record, uvo.id, uvo.id,
props.voiceProp.voiceConf, vu.userId, MeetingStatus2x.isMeetingMuted(liveMeeting.status)))
}
}
}
@ -456,16 +466,17 @@ trait UsersApp {
def startRecordingVoiceConference() {
if (Users.numUsersInVoiceConference(liveMeeting.users) == 1 &&
mProps.recorded && !liveMeeting.isVoiceRecording) {
liveMeeting.startRecordingVoice()
log.info("Send START RECORDING voice conf. meetingId=" + mProps.meetingID + " voice conf=" + mProps.voiceBridge)
outGW.send(new StartRecordingVoiceConf(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
props.recordProp.record &&
!MeetingStatus2x.isVoiceRecording(liveMeeting.status)) {
MeetingStatus2x.startRecordingVoice(liveMeeting.status)
log.info("Send START RECORDING voice conf. meetingId=" + props.meetingProp.intId + " voice conf=" + props.voiceProp.voiceConf)
outGW.send(new StartRecordingVoiceConf(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf))
}
}
def switchUserToPhoneUser(msg: UserJoinedVoiceConfMessage) = {
log.info("User has been disconnected but still in voice conf. Switching to phone user. meetingId="
+ mProps.meetingID + " callername=" + msg.callerIdName
+ props.meetingProp.intId + " callername=" + msg.callerIdName
+ " userId=" + msg.userId + " extUserId=" + msg.externUserId)
Users.findWithId(msg.userId, liveMeeting.users) match {
@ -473,13 +484,13 @@ trait UsersApp {
val nu = Users.switchUserToPhoneUser(user, liveMeeting.users, msg.voiceUserId, msg.userId, msg.callerIdName,
msg.callerIdNum, msg.muted, msg.talking, msg.avatarURL, msg.listenOnly)
log.info("User joined voice. meetingId=" + mProps.meetingID + " userId=" + user.id + " user=" + nu)
outGW.send(new UserJoinedVoice(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nu))
log.info("User joined voice. meetingId=" + props.meetingProp.intId + " userId=" + user.id + " user=" + nu)
outGW.send(new UserJoinedVoice(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, nu))
if (liveMeeting.meetingModel.isMeetingMuted()) {
outGW.send(new MuteVoiceUser(mProps.meetingID, mProps.recorded,
nu.id, nu.id, mProps.voiceBridge,
nu.voiceUser.userId, liveMeeting.meetingModel.isMeetingMuted()))
if (MeetingStatus2x.isMeetingMuted(liveMeeting.status)) {
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record,
nu.id, nu.id, props.voiceProp.voiceConf,
nu.voiceUser.userId, MeetingStatus2x.isMeetingMuted(liveMeeting.status)))
}
}
case None => {
@ -489,7 +500,7 @@ trait UsersApp {
}
def handleUserJoinedVoiceConfMessage(msg: UserJoinedVoiceConfMessage) = {
log.info("Received user joined voice. meetingId=" + mProps.meetingID + " callername=" + msg.callerIdName
log.info("Received user joined voice. meetingId=" + props.meetingProp.intId + " callername=" + msg.callerIdName
+ " userId=" + msg.userId + " extUserId=" + msg.externUserId)
Users.findWithId(msg.userId, liveMeeting.users) match {
@ -500,12 +511,12 @@ trait UsersApp {
val nu = Users.restoreMuteState(user, liveMeeting.users, msg.voiceUserId, msg.userId, msg.callerIdName,
msg.callerIdNum, msg.muted, msg.talking, msg.avatarURL, msg.listenOnly)
log.info("User joined voice. meetingId=" + mProps.meetingID + " userId=" + user.id + " user=" + nu)
outGW.send(new UserJoinedVoice(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nu))
log.info("User joined voice. meetingId=" + props.meetingProp.intId + " userId=" + user.id + " user=" + nu)
outGW.send(new UserJoinedVoice(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, nu))
if (liveMeeting.meetingModel.isMeetingMuted() || previouslyMuted) {
outGW.send(new MuteVoiceUser(mProps.meetingID, mProps.recorded,
nu.id, nu.id, mProps.voiceBridge,
if (MeetingStatus2x.isMeetingMuted(liveMeeting.status) || previouslyMuted) {
outGW.send(new MuteVoiceUser(props.meetingProp.intId, props.recordProp.record,
nu.id, nu.id, props.voiceProp.voiceConf,
nu.voiceUser.userId, true))
}
@ -520,30 +531,31 @@ trait UsersApp {
def stopRecordingVoiceConference() {
if (Users.numUsersInVoiceConference(liveMeeting.users) == 0 &&
mProps.recorded && liveMeeting.isVoiceRecording) {
liveMeeting.stopRecordingVoice()
log.info("Send STOP RECORDING voice conf. meetingId=" + mProps.meetingID + " voice conf=" + mProps.voiceBridge)
outGW.send(new StopRecordingVoiceConf(mProps.meetingID, mProps.recorded,
mProps.voiceBridge, liveMeeting.meetingModel.getVoiceRecordingFilename()))
props.recordProp.record &&
MeetingStatus2x.isVoiceRecording(liveMeeting.status)) {
MeetingStatus2x.stopRecordingVoice(liveMeeting.status)
log.info("Send STOP RECORDING voice conf. meetingId=" + props.meetingProp.intId + " voice conf=" + props.voiceProp.voiceConf)
outGW.send(new StopRecordingVoiceConf(props.meetingProp.intId, props.recordProp.record,
props.voiceProp.voiceConf, MeetingStatus2x.getVoiceRecordingFilename(liveMeeting.status)))
}
}
def handleUserLeftVoiceConfMessage(msg: UserLeftVoiceConfMessage) {
log.info("Received user left voice conf. meetingId=" + mProps.meetingID + " voice conf=" + msg.voiceConfId
log.info("Received user left voice conf. meetingId=" + props.meetingProp.intId + " voice conf=" + msg.voiceConfId
+ " userId=" + msg.voiceUserId)
for {
user <- Users.getUserWithVoiceUserId(msg.voiceUserId, liveMeeting.users)
nu = Users.resetVoiceUser(user, liveMeeting.users)
} yield {
log.info("User left voice conf. meetingId=" + mProps.meetingID + " userId=" + nu.id + " user=" + nu)
outGW.send(new UserLeftVoice(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nu))
log.info("User left voice conf. meetingId=" + props.meetingProp.intId + " userId=" + nu.id + " user=" + nu)
outGW.send(new UserLeftVoice(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, nu))
if (user.phoneUser) {
for {
userLeaving <- Users.userLeft(user.id, liveMeeting.users)
} yield {
outGW.send(new UserLeft(mProps.meetingID, mProps.recorded, userLeaving))
outGW.send(new UserLeft(props.meetingProp.intId, props.recordProp.record, userLeaving))
}
}
}
@ -557,9 +569,9 @@ trait UsersApp {
user <- Users.getUserWithVoiceUserId(msg.voiceUserId, liveMeeting.users)
nu = Users.setUserMuted(user, liveMeeting.users, msg.muted)
} yield {
log.info("User muted in voice conf. meetingId=" + mProps.meetingID + " userId=" + nu.id + " user=" + nu)
log.info("User muted in voice conf. meetingId=" + props.meetingProp.intId + " userId=" + nu.id + " user=" + nu)
outGW.send(new UserVoiceMuted(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nu))
outGW.send(new UserVoiceMuted(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, nu))
}
}
@ -568,7 +580,7 @@ trait UsersApp {
user <- Users.getUserWithVoiceUserId(msg.voiceUserId, liveMeeting.users)
nv = Users.setUserTalking(user, liveMeeting.users, msg.talking)
} yield {
outGW.send(new UserVoiceTalking(mProps.meetingID, mProps.recorded, mProps.voiceBridge, nv))
outGW.send(new UserVoiceTalking(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf, nv))
}
}
@ -578,14 +590,14 @@ trait UsersApp {
def assignNewPresenter(newPresenterID: String, newPresenterName: String, assignedBy: String) {
// Stop poll if one is running as presenter left.
handleStopPollRequest(StopPollRequest(mProps.meetingID, assignedBy))
handleStopPollRequest(StopPollRequest(props.meetingProp.intId, assignedBy))
def removePresenterRightsToCurrentPresenter(): Unit = {
for {
curPres <- Users.getCurrentPresenter(liveMeeting.users)
} yield {
Users.unbecomePresenter(curPres.id, liveMeeting.users)
outGW.send(new UserStatusChange(mProps.meetingID, mProps.recorded, curPres.id, "presenter", false: java.lang.Boolean))
outGW.send(new UserStatusChange(props.meetingProp.intId, props.recordProp.record, curPres.id, "presenter", false: java.lang.Boolean))
}
}
@ -594,9 +606,9 @@ trait UsersApp {
} yield {
removePresenterRightsToCurrentPresenter()
Users.becomePresenter(newPres.id, liveMeeting.users)
liveMeeting.setCurrentPresenterInfo(new Presenter(newPresenterID, newPresenterName, assignedBy))
outGW.send(new PresenterAssigned(mProps.meetingID, mProps.recorded, new Presenter(newPresenterID, newPresenterName, assignedBy)))
outGW.send(new UserStatusChange(mProps.meetingID, mProps.recorded, newPresenterID, "presenter", true: java.lang.Boolean))
MeetingStatus2x.setCurrentPresenterInfo(liveMeeting.status, new Presenter(newPresenterID, newPresenterName, assignedBy))
outGW.send(new PresenterAssigned(props.meetingProp.intId, props.recordProp.record, new Presenter(newPresenterID, newPresenterName, assignedBy)))
outGW.send(new UserStatusChange(props.meetingProp.intId, props.recordProp.record, newPresenterID, "presenter", true: java.lang.Boolean))
}
}
@ -613,9 +625,9 @@ trait UsersApp {
if (msg.response == true) {
val nu = Users.setWaitingForAcceptance(user, liveMeeting.users, false)
RegisteredUsers.updateRegUser(nu, liveMeeting.registeredUsers)
outGW.send(new UserJoined(mProps.meetingID, mProps.recorded, nu))
outGW.send(new UserJoined(props.meetingProp.intId, props.recordProp.record, nu))
} else {
outGW.send(new GuestAccessDenied(mProps.meetingID, mProps.recorded, user.id))
outGW.send(new GuestAccessDenied(props.meetingProp.intId, props.recordProp.record, user.id))
}
}
}

View File

@ -1,73 +0,0 @@
package org.bigbluebutton.core.apps
import org.bigbluebutton.core.util.RandomStringGenerator
import org.bigbluebutton.core.api.Presenter
import org.bigbluebutton.core.models._
class UsersModel {
// private var uservos = new Users
// private var regUsers = new RegisteredUsers
/* When reconnecting SIP global audio, users may receive the connection message
* before the disconnection message.
* This variable is a connection counter that should control this scenario.
*/
private var globalAudioConnectionCounter = new collection.immutable.HashMap[String, Integer]
private var locked = false
private var meetingMuted = false
private var recordingVoice = false
private var currentPresenter = new Presenter("system", "system", "system")
def setCurrentPresenterInfo(pres: Presenter) {
currentPresenter = pres
}
def getCurrentPresenterInfo(): Presenter = {
currentPresenter
}
def addGlobalAudioConnection(userID: String): Boolean = {
globalAudioConnectionCounter.get(userID) match {
case Some(vc) => {
globalAudioConnectionCounter += userID -> (vc + 1)
false
}
case None => {
globalAudioConnectionCounter += userID -> 1
true
}
}
}
def removeGlobalAudioConnection(userID: String): Boolean = {
globalAudioConnectionCounter.get(userID) match {
case Some(vc) => {
if (vc == 1) {
globalAudioConnectionCounter -= userID
true
} else {
globalAudioConnectionCounter += userID -> (vc - 1)
false
}
}
case None => {
false
}
}
}
def startRecordingVoice() {
recordingVoice = true
}
def stopRecordingVoice() {
recordingVoice = false
}
def isVoiceRecording: Boolean = {
recordingVoice
}
}

View File

@ -42,44 +42,44 @@ trait WhiteboardApp {
}
if (liveMeeting.wbModel.hasWhiteboard(wbId)) {
// println("WhiteboardApp::handleSendWhiteboardAnnotationRequest - num shapes [" + wb.shapes.length + "]")
outGW.send(new SendWhiteboardAnnotationEvent(mProps.meetingID, mProps.recorded, msg.requesterID, wbId, shape))
outGW.send(new SendWhiteboardAnnotationEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, wbId, shape))
}
}
def handleSendCursorPositionRequest(msg: SendCursorPositionRequest) {
outGW.send(new CursorPositionUpdatedEvent(mProps.meetingID, mProps.recorded, msg.requesterID, msg.xPercent, msg.yPercent))
outGW.send(new CursorPositionUpdatedEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.xPercent, msg.yPercent))
}
def handleGetWhiteboardShapesRequest(msg: GetWhiteboardShapesRequest) {
//println("WB: Received page history [" + msg.whiteboardId + "]")
val history = liveMeeting.wbModel.getHistory(msg.whiteboardId);
if (history.length > 0) {
outGW.send(new GetWhiteboardShapesReply(mProps.meetingID, mProps.recorded, msg.requesterID, msg.whiteboardId, history, msg.replyTo))
outGW.send(new GetWhiteboardShapesReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.whiteboardId, history, msg.replyTo))
}
}
def handleClearWhiteboardRequest(msg: ClearWhiteboardRequest) {
//println("WB: Received clear whiteboard")
liveMeeting.wbModel.clearWhiteboard(msg.whiteboardId, msg.requesterID) foreach { fullClear =>
outGW.send(new ClearWhiteboardEvent(mProps.meetingID, mProps.recorded, msg.requesterID, msg.whiteboardId, fullClear))
outGW.send(new ClearWhiteboardEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.whiteboardId, fullClear))
}
}
def handleUndoWhiteboardRequest(msg: UndoWhiteboardRequest) {
// println("WB: Received undo whiteboard")
liveMeeting.wbModel.undoWhiteboard(msg.whiteboardId, msg.requesterID) foreach { last =>
outGW.send(new UndoWhiteboardEvent(mProps.meetingID, mProps.recorded, msg.requesterID, msg.whiteboardId, last.id))
outGW.send(new UndoWhiteboardEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.whiteboardId, last.id))
}
}
def handleModifyWhiteboardAccessRequest(msg: ModifyWhiteboardAccessRequest) {
liveMeeting.wbModel.modifyWhiteboardAccess(msg.multiUser)
outGW.send(new ModifiedWhiteboardAccessEvent(mProps.meetingID, mProps.recorded, msg.requesterID, msg.multiUser))
outGW.send(new ModifiedWhiteboardAccessEvent(props.meetingProp.intId, props.recordProp.record, msg.requesterID, msg.multiUser))
}
def handleGetWhiteboardAccessRequest(msg: GetWhiteboardAccessRequest) {
val multiUser = liveMeeting.wbModel.getWhiteboardAccess()
outGW.send(new GetWhiteboardAccessReply(mProps.meetingID, mProps.recorded, msg.requesterID, multiUser))
outGW.send(new GetWhiteboardAccessReply(props.meetingProp.intId, props.recordProp.record, msg.requesterID, multiUser))
}
}

View File

@ -1,9 +1,7 @@
package org.bigbluebutton.core.apps
import java.util.ArrayList;
import org.bigbluebutton.core.util.jhotdraw.BezierWrapper
import scala.collection.immutable.List
import scala.collection.immutable.HashMap
@ -208,4 +206,4 @@ class WhiteboardModel {
def getWhiteboardAccess(): Boolean = {
_multiUser
}
}
}

View File

@ -0,0 +1,32 @@
package org.bigbluebutton.core.bus
import akka.actor.ActorRef
import akka.event.{ EventBus, LookupClassification }
import org.bigbluebutton.common2.messages.BbbCommonEnvCoreMsg
case class BbbMsgEvent(val topic: String, val payload: BbbCommonEnvCoreMsg)
class BbbMsgRouterEventBus extends EventBus with LookupClassification {
type Event = BbbMsgEvent
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

@ -2,7 +2,6 @@ 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

View File

@ -0,0 +1,31 @@
package org.bigbluebutton.core.bus
import akka.actor.ActorRef
import akka.event.{ EventBus, LookupClassification }
case class ReceivedJsonMessage(channel: String, data: String)
case class IncomingJsonMessage(val topic: String, val payload: ReceivedJsonMessage)
class IncomingJsonMessageBus extends EventBus with LookupClassification {
type Event = IncomingJsonMessage
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,31 @@
package org.bigbluebutton.core.bus
import akka.actor.ActorRef
import akka.event.{ EventBus, LookupClassification }
import org.bigbluebutton.common2.messages.{ BbbCommonEnvCoreMsg }
case class BbbOutMessage(val topic: String, val payload: BbbCommonEnvCoreMsg)
class OutEventBus2 extends EventBus with LookupClassification {
type Event = BbbOutMessage
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

@ -2,7 +2,6 @@ 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._

View File

@ -0,0 +1,125 @@
package org.bigbluebutton.core.domain
import com.softwaremill.quicklens._
import org.bigbluebutton.core.api.Permissions
case class MeetingProperties(
id: String,
extId: String,
name: String,
recorded: Boolean,
voiceConf: String,
duration: Int,
autoStartRecording: Boolean,
allowStartStopRecording: Boolean,
moderatorPass: String,
viewerPass: String,
createTime: Long,
createDate: String,
isBreakout: Boolean)
case class MeetingProperties2x(
id: String,
extId: String,
name: String,
voiceConf: String,
duration: Int,
maxUsers: Int,
allowVoiceOnly: Boolean,
isBreakout: Boolean,
extensionProp: MeetingExtensionProp,
recordingProp: MeetingRecordingProp)
case class MeetingRecordingProp(
recorded: Boolean = false,
autoStartRecording: Boolean = false,
allowStartStopRecording: Boolean = true)
case class MeetingExtensionProp(
maxExtensions: Int = 0,
extendByMinutes: Int = 20,
sendNotice: Boolean = true)
case class MeetingRecordingStatus(
recording: Boolean = false,
voiceRecordingFilename: String = "")
case class MeetingExtensionStatus(
numExtensions: Int = 0,
sent15MinNotice: Boolean = false,
sent10MinNotice: Boolean = false,
sent5MinNotice: Boolean = false)
case class Meeting3x(permissions: Permissions,
isRecording: Boolean = false,
muted: Boolean = false,
ended: Boolean = false,
hasLastWebUserLeft: Boolean = false,
lastWebUserLeftOnTimestamp: Long = 0L,
voiceRecordingFilename: String = "",
startedOn: Long = 0L,
pinNumbers: Set[String] = Set.empty,
lastGeneratedPin: Int = 0,
breakoutRoomsStartedOn: Long = 0L,
breakoutRoomsDurationInMinutes: Int = 120,
extensionStatus: MeetingExtensionStatus = new MeetingExtensionStatus,
recordingStatus: MeetingRecordingStatus = new MeetingRecordingStatus)
object Meeting3x {
def isExtensionAllowed(
extension: MeetingExtensionProp,
status: MeetingExtensionStatus): Boolean = status.numExtensions < extension.maxExtensions
def incNumExtension(extension: MeetingExtensionProp, status: MeetingExtensionStatus): MeetingExtensionStatus = {
if (status.numExtensions < extension.maxExtensions) {
modify(status)(_.numExtensions).setTo(status.numExtensions + 1)
}
status
}
def fifteenMinutesNoticeSent(extension: MeetingExtensionStatus): MeetingExtensionStatus = {
modify(extension)(_.sent15MinNotice).setTo(true)
}
def tenMinutesNoticeSent(extension: MeetingExtensionStatus): MeetingExtensionStatus = {
modify(extension)(_.sent10MinNotice).setTo(true)
}
def fiveMinutesNoticeSent(extension: MeetingExtensionStatus): MeetingExtensionStatus = {
modify(extension)(_.sent5MinNotice).setTo(true)
}
def mute(meeting: Meeting3x): Meeting3x = {
modify(meeting)(_.muted).setTo(true)
}
def unMute(meeting: Meeting3x): Meeting3x = {
modify(meeting)(_.muted).setTo(false)
}
def recordingStarted(meeting: Meeting3x): Meeting3x = {
modify(meeting)(_.isRecording).setTo(true)
}
def recordingStopped(meeting: Meeting3x): Meeting3x = {
modify(meeting)(_.isRecording).setTo(false)
}
}
object PinNumberGenerator {
def generatePin(conf: String, meeting: Meeting3x): String = {
def inc(curPin: Int): Int = {
if ((curPin + 1) < 1000) curPin + 1
else 1
}
def genAvailablePin(): Int = {
val pin = conf.toInt + inc(meeting.lastGeneratedPin)
if (meeting.pinNumbers.contains(pin.toString)) genAvailablePin
pin
}
genAvailablePin.toString
}
}

View File

@ -37,10 +37,13 @@ class PublicChats {
}
object PublicChat {
def append(chat: PublicChat, msg: PublicChatMessage): PublicChatMessage = {
chat.append(msg)
}
def getMessages(chat: PublicChat): Vector[PublicChatMessage] = {
chat.toVector
}
}
class PublicChat(val id: String) {

View File

@ -254,33 +254,12 @@ class Users {
case class UserIdAndName(id: String, name: String)
case class UserVO(
id: String,
externalId: String,
name: String,
role: String,
guest: Boolean,
authed: Boolean,
waitingForAcceptance: Boolean,
emojiStatus: String,
presenter: Boolean,
hasStream: Boolean,
locked: Boolean,
webcamStreams: Set[String],
phoneUser: Boolean,
voiceUser: VoiceUser,
listenOnly: Boolean,
avatarURL: String,
case class UserVO(id: String, externalId: String, name: String, role: String,
guest: Boolean, authed: Boolean, waitingForAcceptance: Boolean, emojiStatus: String,
presenter: Boolean, hasStream: Boolean, locked: Boolean, webcamStreams: Set[String],
phoneUser: Boolean, voiceUser: VoiceUser, listenOnly: Boolean, avatarURL: String,
joinedWeb: Boolean)
case class VoiceUser(
userId: String,
webUserId: String,
callerName: String,
callerNum: String,
joined: Boolean,
locked: Boolean,
muted: Boolean,
talking: Boolean,
avatarURL: String,
listenOnly: Boolean)
case class VoiceUser(userId: String, webUserId: String, callerName: String,
callerNum: String, joined: Boolean, locked: Boolean, muted: Boolean,
talking: Boolean, avatarURL: String, listenOnly: Boolean)

View File

@ -0,0 +1,31 @@
package org.bigbluebutton.core.models
object Webcams {
def findWithStreamId(webcams: Webcams, streamId: String): Option[WebcamStream] = {
webcams.toVector.find(w => w.stream.id == streamId)
}
def findWebcamsForUser(webcams: Webcams, userId: String): Vector[WebcamStream] = {
webcams.toVector.filter(w => w.userId == userId)
}
}
class Webcams {
private var webcams: collection.immutable.HashMap[String, WebcamStream] = new collection.immutable.HashMap[String, WebcamStream]
private def toVector: Vector[WebcamStream] = webcams.values.toVector
private def save(webcam: WebcamStream): WebcamStream = {
webcams += webcam.stream.id -> webcam
webcam
}
private def remove(streamId: String): Option[WebcamStream] = {
val webcam = webcams.get(streamId)
webcam foreach (u => webcams -= streamId)
webcam
}
}
case class WebcamStream(userId: String, stream: Stream)

View File

@ -0,0 +1,19 @@
package org.bigbluebutton.core.pubsub
import akka.actor.{ Actor, ActorLogging, Props }
import org.bigbluebutton.core.MessageSender
//import org.bigbluebutton.common2.messages.MeetingCreatedEvt
object JsonMsgSenderActor {
def props(msgSender: MessageSender): Props =
Props(classOf[JsonMsgSenderActor], msgSender)
}
class JsonMsgSenderActor(val service: MessageSender)
extends Actor with ActorLogging {
def receive = {
// case msg: MeetingCreatedEvt => println(msg)
case msg: String => println(msg)
}
}

View File

@ -0,0 +1,5 @@
package org.bigbluebutton.core.pubsub.handlers
class BbbMsgHdlr {
}

View File

@ -1,9 +1,7 @@
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

View File

@ -1,13 +1,9 @@
package org.bigbluebutton.core.pubsub.senders
import scala.collection.mutable.HashMap
import org.bigbluebutton.core.api._
import com.google.gson.Gson
import scala.collection.mutable.HashMap
import collection.JavaConverters._
import scala.collection.JavaConversions._
import java.util.ArrayList
import org.bigbluebutton.common.messages.MessagingConstants
import org.bigbluebutton.core.messaging.Util
import org.bigbluebutton.common.messages.ChatKeyUtil

View File

@ -4,10 +4,6 @@ import org.bigbluebutton.core.api._
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 = {
val payload = new java.util.HashMap[String, Any]()

View File

@ -3,7 +3,6 @@ package org.bigbluebutton.core.pubsub.senders
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.messaging.Util
import org.bigbluebutton.core.apps.Page
import collection.JavaConverters._
import scala.collection.JavaConversions._
object PesentationMessageToJsonConverter {

View File

@ -0,0 +1,44 @@
package org.bigbluebutton.core.pubsub.senders
import com.fasterxml.jackson.databind.JsonNode
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.common2.messages._
import org.bigbluebutton.core.bus.{ BbbMsgEvent, BbbMsgRouterEventBus, ReceivedJsonMessage }
import scala.util.{ Failure, Success }
trait ReceivedJsonMsgDeserializer extends SystemConfiguration {
this: ReceivedJsonMsgHandlerActor =>
object JsonDeserializer extends Deserializer
def deserializeCreateMeetingReqMsg(jsonNode: JsonNode): Option[CreateMeetingReqMsg] = {
JsonDeserializer.toBbbCommonMsg[CreateMeetingReqMsg](jsonNode) match {
case Success(msg) => Some(msg.asInstanceOf[CreateMeetingReqMsg])
case Failure(ex) =>
log.error("Failed to CreateMeetingReqMsg message " + ex)
None
}
}
def routeValidateAuthTokenReqMsg(jsonNode: JsonNode): Option[ValidateAuthTokenReqMsg] = {
JsonDeserializer.toBbbCommonMsg[ValidateAuthTokenReqMsg](jsonNode) match {
case Success(msg) => Some(msg.asInstanceOf[ValidateAuthTokenReqMsg])
case Failure(ex) =>
log.error("Failed to ValidateAuthTokenReqMsg message " + ex)
None
}
}
def routeRegisterUserReqMsg(jsonNode: JsonNode): Option[RegisterUserReqMsg] = {
JsonDeserializer.toBbbCommonMsg[RegisterUserReqMsg](jsonNode) match {
case Success(msg) =>
// Route via meeting manager as there is a race condition if we send directly to meeting
// because the meeting actor might not have been created yet.
Some(msg.asInstanceOf[RegisterUserReqMsg])
case Failure(ex) =>
log.error("Failed to RegisterUserReqMsg message " + ex)
None
}
}
}

View File

@ -0,0 +1,70 @@
package org.bigbluebutton.core.pubsub.senders
import akka.actor.{ Actor, ActorLogging, Props }
import org.bigbluebutton.SystemConfiguration
import com.fasterxml.jackson.databind.JsonNode
import org.bigbluebutton.common2.messages.{ BbbCoreEnvelope, CreateMeetingReqMsg, RegisterUserReqMsg, ValidateAuthTokenReqMsg }
import org.bigbluebutton.core.bus._
import org.bigbluebutton.core2.ReceivedMessageRouter
import scala.util.{ Failure, Success }
object ReceivedJsonMsgHandlerActor {
def props(eventBus: BbbMsgRouterEventBus, incomingJsonMessageBus: IncomingJsonMessageBus): Props =
Props(classOf[ReceivedJsonMsgHandlerActor], eventBus, incomingJsonMessageBus)
}
class ReceivedJsonMsgHandlerActor(
val eventBus: BbbMsgRouterEventBus,
val incomingJsonMessageBus: IncomingJsonMessageBus)
extends Actor with ActorLogging
with SystemConfiguration
with ReceivedJsonMsgDeserializer
with ReceivedMessageRouter {
def receive = {
case msg: ReceivedJsonMessage =>
log.debug("handling {} - {}", msg.channel, msg.data)
handleReceivedJsonMessage(msg)
case _ => // do nothing
}
def handleReceivedJsonMessage(msg: ReceivedJsonMessage): Unit = {
for {
envJsonNode <- JsonDeserializer.toBbbCommonEnvJsNodeMsg(msg.data)
} yield route(envJsonNode.envelope, envJsonNode.core)
}
def route(envelope: BbbCoreEnvelope, jsonNode: JsonNode): Unit = {
log.debug("*************** Route envelope name " + envelope.name)
envelope.name match {
case CreateMeetingReqMsg.NAME =>
log.debug("**************** Route CreateMeetingReqMsg")
for {
m <- deserializeCreateMeetingReqMsg(jsonNode)
} yield {
log.debug("************ Sending CreateMeetingReqMsg")
send(envelope, m)
}
case ValidateAuthTokenReqMsg.NAME =>
log.debug("**************** Route ValidateAuthTokenReqMsg")
for {
m <- routeValidateAuthTokenReqMsg(jsonNode)
} yield {
log.debug("************ Sending ValidateAuthTokenReqMsg")
send(envelope, m)
}
case RegisterUserReqMsg.NAME =>
log.debug("**************** Route RegisterUserReqMsg")
for {
m <- routeRegisterUserReqMsg(jsonNode)
} yield {
log.debug("************ Sending RegisterUserReqMsg")
send(envelope, m)
}
case _ =>
log.debug("************ Cannot route envelope name " + envelope.name)
// do nothing
}
}
}

View File

@ -2,7 +2,6 @@ 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._
object SharedNotesMessageToJsonConverter {

View File

@ -1,13 +1,9 @@
package org.bigbluebutton.core.pubsub.senders
import org.bigbluebutton.core.api._
import org.bigbluebutton.common.messages.MessagingConstants
import org.bigbluebutton.core.messaging.Util
import com.google.gson.Gson
import org.bigbluebutton.core.models.{ RegisteredUser, UserVO }
import collection.JavaConverters._
import scala.collection.JavaConversions._
import scala.collection.JavaConverters._
object UsersMessageToJsonConverter {
private def userToMap(user: UserVO): java.util.Map[String, Any] = {
@ -38,9 +34,9 @@ object UsersMessageToJsonConverter {
vuser += "muted" -> user.voiceUser.muted
vuser += "talking" -> user.voiceUser.talking
wuser.put("voiceUser", mapAsJavaMap(vuser))
wuser.put("voiceUser", mapAsJavaMapConverter(vuser).asJava)
mapAsJavaMap(wuser)
mapAsJavaMapConverter(wuser).asJava
}
private def registeredUserToMap(user: RegisteredUser): java.util.Map[String, Any] = {
@ -54,7 +50,7 @@ object UsersMessageToJsonConverter {
wuser += "guest" -> user.guest
wuser += "waiting_for_acceptance" -> user.waitingForAcceptance
mapAsJavaMap(wuser)
mapAsJavaMapConverter(wuser).asJava
}
private def buildPermissionsHashMap(perms: Permissions): java.util.HashMap[String, java.lang.Boolean] = {

View File

@ -3,8 +3,7 @@ package org.bigbluebutton.core.pubsub.senders
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.messaging.Util
import org.bigbluebutton.core.apps.AnnotationVO
import collection.JavaConverters._
import scala.collection.JavaConversions._
import scala.collection.JavaConverters._
object WhiteboardMessageToJsonConverter {
private def shapeToMap(shape: AnnotationVO): java.util.Map[String, Any] = {
@ -20,9 +19,9 @@ object WhiteboardMessageToJsonConverter {
for ((key, value) <- shape.shape) {
shapeMap += key -> value
}
res += "shape" -> mapAsJavaMap(shapeMap)
res += "shape" -> mapAsJavaMapConverter(shapeMap).asJava
mapAsJavaMap(res)
mapAsJavaMapConverter(res).asJava
}
def getWhiteboardShapesReplyToJson(msg: GetWhiteboardShapesReply): String = {

View File

@ -2,78 +2,46 @@ package org.bigbluebutton.core.running
import java.util.concurrent.TimeUnit
import akka.actor.ActorContext
import akka.event.Logging
import org.bigbluebutton.common2.domain.DefaultProps
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.apps._
import org.bigbluebutton.core.models.{ RegisteredUsers, Users }
import org.bigbluebutton.core.{ MeetingModel, MeetingProperties }
import org.bigbluebutton.core2.MeetingStatus2x
class LiveMeeting(val mProps: MeetingProperties,
val chatModel: ChatModel,
val layoutModel: LayoutModel,
val meetingModel: MeetingModel,
private val usersModel: UsersModel,
val users: Users,
val registeredUsers: RegisteredUsers,
val pollModel: PollModel,
val wbModel: WhiteboardModel,
val presModel: PresentationModel,
val breakoutModel: BreakoutRoomModel,
val captionModel: CaptionModel,
val notesModel: SharedNotesModel)(implicit val context: ActorContext) {
val log = Logging(context.system, getClass)
class LiveMeeting(val props: DefaultProps,
val status: MeetingStatus2x,
val chatModel: ChatModel,
val layoutModel: LayoutModel,
val users: Users,
val registeredUsers: RegisteredUsers,
val pollModel: PollModel,
val wbModel: WhiteboardModel,
val presModel: PresentationModel,
val breakoutModel: BreakoutRoomModel,
val captionModel: CaptionModel,
val notesModel: SharedNotesModel)
extends ChatModelTrait {
def hasMeetingEnded(): Boolean = {
meetingModel.hasMeetingEnded()
MeetingStatus2x.hasMeetingEnded(status)
}
def webUserJoined() {
if (Users.numWebUsers(users) > 0) {
meetingModel.resetLastWebUserLeftOn()
MeetingStatus2x.resetLastWebUserLeftOn(status)
}
}
def setCurrentPresenterInfo(pres: Presenter) {
usersModel.setCurrentPresenterInfo(pres)
}
def getCurrentPresenterInfo(): Presenter = {
usersModel.getCurrentPresenterInfo()
}
def addGlobalAudioConnection(userID: String): Boolean = {
usersModel.addGlobalAudioConnection(userID)
}
def removeGlobalAudioConnection(userID: String): Boolean = {
usersModel.removeGlobalAudioConnection(userID)
}
def startRecordingVoice() {
usersModel.startRecordingVoice()
}
def stopRecordingVoice() {
usersModel.stopRecordingVoice()
}
def isVoiceRecording: Boolean = {
usersModel.isVoiceRecording
}
def startCheckingIfWeNeedToEndVoiceConf() {
if (Users.numWebUsers(users) == 0 && !mProps.isBreakout) {
meetingModel.lastWebUserLeft()
log.debug("MonitorNumberOfWebUsers started for meeting [" + mProps.meetingID + "]")
if (Users.numWebUsers(users) == 0 && !props.meetingProp.isBreakout) {
MeetingStatus2x.lastWebUserLeft(status)
}
}
def sendTimeRemainingNotice() {
val now = timeNowInSeconds
if (mProps.duration > 0 && (((meetingModel.startedOn + mProps.duration) - now) < 15)) {
if (props.durationProps.duration > 0 && (((MeetingStatus2x.startedOn(status) + props.durationProps.duration) - now) < 15)) {
// log.warning("MEETING WILL END IN 15 MINUTES!!!!")
}
}
@ -87,15 +55,15 @@ class LiveMeeting(val mProps: MeetingProperties,
}
def lockLayout(lock: Boolean) {
meetingModel.lockLayout(lock)
MeetingStatus2x.lockLayout(status, lock)
}
def newPermissions(np: Permissions) {
meetingModel.setPermissions(np)
MeetingStatus2x.setPermissions(status, np)
}
def permissionsEqual(other: Permissions): Boolean = {
meetingModel.permissionsEqual(other)
MeetingStatus2x.permissionsEqual(status, other)
}
}

View File

@ -2,27 +2,30 @@ package org.bigbluebutton.core.running
import java.io.{ PrintWriter, StringWriter }
import akka.actor.Actor
import akka.actor._
import akka.actor.ActorLogging
import akka.actor.Props
import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy.Resume
import akka.util.Timeout
import org.bigbluebutton.common2.domain.DefaultProps
import org.bigbluebutton.common2.messages.MessageBody.ValidateAuthTokenRespMsgBody
import org.bigbluebutton.common2.messages._
import org.bigbluebutton.core._
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.apps._
import org.bigbluebutton.core.bus._
import org.bigbluebutton.core.models.Users
import org.bigbluebutton.core.models.{ RegisteredUsers, Users }
import org.bigbluebutton.core2.MeetingStatus2x
import scala.concurrent.duration._
object MeetingActor {
def props(mProps: MeetingProperties,
def props(props: DefaultProps,
eventBus: IncomingEventBus,
outGW: OutMessageGateway, liveMeeting: LiveMeeting): Props =
Props(classOf[MeetingActor], mProps, eventBus, outGW, liveMeeting)
Props(classOf[MeetingActor], props, eventBus, outGW, liveMeeting)
}
class MeetingActor(val mProps: MeetingProperties,
class MeetingActor(val props: DefaultProps,
val eventBus: IncomingEventBus,
val outGW: OutMessageGateway, val liveMeeting: LiveMeeting)
extends Actor with ActorLogging
@ -44,18 +47,27 @@ class MeetingActor(val mProps: MeetingProperties,
* Put the internal message injector into another actor so this
* actor is easy to test.
*/
var actorMonitor = context.actorOf(MeetingActorInternal.props(mProps, eventBus, outGW), "actorMonitor-" + mProps.meetingID)
var actorMonitor = context.actorOf(MeetingActorInternal.props(props, eventBus, outGW),
"actorMonitor-" + props.meetingProp.intId)
/** Subscribe to meeting and voice events. **/
eventBus.subscribe(actorMonitor, mProps.meetingID)
eventBus.subscribe(actorMonitor, mProps.voiceBridge)
eventBus.subscribe(actorMonitor, mProps.deskshareBridge)
eventBus.subscribe(actorMonitor, props.meetingProp.intId)
eventBus.subscribe(actorMonitor, props.voiceProp.voiceConf)
eventBus.subscribe(actorMonitor, props.screenshareProps.screenshareConf)
def receive = {
//=============================
// 2x messages
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
case msg: RegisterUserReqMsg => handleRegisterUserReqMsg(msg)
//======================================
//=======================================
// old messages
case msg: ActivityResponse => handleActivityResponse(msg)
case msg: MonitorNumberOfUsers => handleMonitorNumberOfUsers(msg)
case msg: ValidateAuthToken => handleValidateAuthToken(msg)
case msg: RegisterUser => handleRegisterUser(msg)
//case msg: RegisterUser => handleRegisterUser(msg)
case msg: UserJoinedVoiceConfMessage => handleUserJoinedVoiceConfMessage(msg)
case msg: UserLeftVoiceConfMessage => handleUserLeftVoiceConfMessage(msg)
case msg: UserMutedInVoiceConfMessage => handleUserMutedInVoiceConfMessage(msg)
@ -164,17 +176,68 @@ class MeetingActor(val mProps: MeetingProperties,
case _ => // do nothing
}
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
msg.core match {
case m: ValidateAuthTokenReqMsg => handleValidateAuthTokenReqMsg(m)
case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m)
case _ => println("***** Cannot handle " + msg.envelope.name)
}
}
def handleRegisterUserReqMsg(msg: RegisterUserReqMsg): Unit = {
log.debug("****** RECEIVED RegisterUserReqMsg msg {}", msg)
if (MeetingStatus2x.hasMeetingEnded(liveMeeting.status)) {
// Check first if the meeting has ended and the user refreshed the client to re-connect.
log.info("Register user failed. Mmeeting has ended. meetingId=" + props.meetingProp.intId +
" userId=" + msg.body.intUserId)
} else {
val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId,
msg.body.name, msg.body.role, msg.body.authToken,
msg.body.avatarURL, msg.body.guest, msg.body.authed, msg.body.guest, liveMeeting.registeredUsers)
log.info("Register user success. meetingId=" + props.meetingProp.intId + " userId=" + msg.body.extUserId + " user=" + regUser)
outGW.send(new UserRegistered(props.meetingProp.intId, props.recordProp.record, regUser))
}
}
def handleValidateAuthTokenReqMsg(msg: ValidateAuthTokenReqMsg): Unit = {
log.debug("****** RECEIVED ValidateAuthTokenReqMsg msg {}", msg)
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka",
"msgType" -> "direct", "meetingId" -> props.meetingProp.intId, "userId" -> msg.body.userId)
val envelope = BbbCoreEnvelope(ValidateAuthTokenRespMsg.NAME, routing)
val header = BbbClientMsgHeader(ValidateAuthTokenRespMsg.NAME, props.meetingProp.intId, msg.body.userId)
RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers) match {
case Some(u) =>
log.info("ValidateToken success. meetingId=" + props.meetingProp.intId + " userId=" + msg.body.userId)
val body = ValidateAuthTokenRespMsgBody(msg.body.userId, msg.body.authToken, true)
val event = ValidateAuthTokenRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
case None =>
log.info("ValidateToken failed. meetingId=" + props.meetingProp.intId + " userId=" + msg.body.userId)
val body = ValidateAuthTokenRespMsgBody(msg.body.userId, msg.body.authToken, false)
val event = ValidateAuthTokenRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent)
}
}
def handleDeskShareRTMPBroadcastStoppedRequest(msg: DeskShareRTMPBroadcastStoppedRequest): Unit = {
log.info("handleDeskShareRTMPBroadcastStoppedRequest: isBroadcastingRTMP=" + liveMeeting.meetingModel
.isBroadcastingRTMP() + " URL:" + liveMeeting.meetingModel.getRTMPBroadcastingUrl())
log.info("handleDeskShareRTMPBroadcastStoppedRequest: isBroadcastingRTMP=" +
MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status) + " URL:" +
MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status))
// only valid if currently broadcasting
if (liveMeeting.meetingModel.isBroadcastingRTMP()) {
if (MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status)) {
log.info("STOP broadcast ALLOWED when isBroadcastingRTMP=true")
liveMeeting.meetingModel.broadcastingRTMPStopped()
MeetingStatus2x.broadcastingRTMPStopped(liveMeeting.status)
// notify viewers that RTMP broadcast stopped
outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, liveMeeting.meetingModel.getRTMPBroadcastingUrl(),
outGW.send(new DeskShareNotifyViewersRTMP(props.meetingProp.intId,
MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status),
msg.videoWidth, msg.videoHeight, false))
} else {
log.info("STOP broadcast NOT ALLOWED when isBroadcastingRTMP=false")
@ -184,42 +247,48 @@ class MeetingActor(val mProps: MeetingProperties,
def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = {
log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting="
+ liveMeeting.meetingModel.isBroadcastingRTMP() + " URL:" + liveMeeting.meetingModel.getRTMPBroadcastingUrl())
if (liveMeeting.meetingModel.isBroadcastingRTMP()) {
+ MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status) + " URL:" +
MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status))
if (MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status)) {
// if the meeting has an ongoing WebRTC Deskshare session, send a notification
outGW.send(new DeskShareNotifyASingleViewer(mProps.meetingID, msg.requesterID, liveMeeting.meetingModel.getRTMPBroadcastingUrl(),
liveMeeting.meetingModel.getDesktopShareVideoWidth(), liveMeeting.meetingModel.getDesktopShareVideoHeight(), true))
outGW.send(new DeskShareNotifyASingleViewer(props.meetingProp.intId, msg.requesterID,
MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status),
MeetingStatus2x.getDesktopShareVideoWidth(liveMeeting.status),
MeetingStatus2x.getDesktopShareVideoHeight(liveMeeting.status), true))
}
}
def handleGetGuestPolicy(msg: GetGuestPolicy) {
outGW.send(new GetGuestPolicyReply(msg.meetingID, mProps.recorded, msg.requesterID, liveMeeting.meetingModel.getGuestPolicy().toString()))
outGW.send(new GetGuestPolicyReply(msg.meetingID, props.recordProp.record,
msg.requesterID, MeetingStatus2x.getGuestPolicy(liveMeeting.status).toString()))
}
def handleSetGuestPolicy(msg: SetGuestPolicy) {
liveMeeting.meetingModel.setGuestPolicy(msg.policy)
liveMeeting.meetingModel.setGuestPolicySetBy(msg.setBy)
outGW.send(new GuestPolicyChanged(msg.meetingID, mProps.recorded, liveMeeting.meetingModel.getGuestPolicy().toString()))
MeetingStatus2x.setGuestPolicy(liveMeeting.status, msg.policy)
MeetingStatus2x.setGuestPolicySetBy(liveMeeting.status, msg.setBy)
outGW.send(new GuestPolicyChanged(msg.meetingID, props.recordProp.record,
MeetingStatus2x.getGuestPolicy(liveMeeting.status).toString()))
}
def handleLogoutEndMeeting(msg: LogoutEndMeeting) {
if (Users.isModerator(msg.userID, liveMeeting.users)) {
handleEndMeeting(EndMeeting(mProps.meetingID))
handleEndMeeting(EndMeeting(props.meetingProp.intId))
}
}
def handleActivityResponse(msg: ActivityResponse) {
log.info("User endorsed that meeting {} is active", mProps.meetingID)
outGW.send(new MeetingIsActive(mProps.meetingID))
log.info("User endorsed that meeting {} is active", props.meetingProp.intId)
outGW.send(new MeetingIsActive(props.meetingProp.intId))
}
def handleEndMeeting(msg: EndMeeting) {
// Broadcast users the meeting will end
outGW.send(new MeetingEnding(msg.meetingId))
liveMeeting.meetingModel.meetingHasEnded
MeetingStatus2x.meetingHasEnded(liveMeeting.status)
outGW.send(new MeetingEnded(msg.meetingId, mProps.recorded, mProps.voiceBridge))
outGW.send(new MeetingEnded(msg.meetingId, props.recordProp.record, props.meetingProp.intId))
}
def handleAllowUserToShareDesktop(msg: AllowUserToShareDesktop): Unit = {
@ -234,69 +303,75 @@ class MeetingActor(val mProps: MeetingProperties,
def handleVoiceConfRecordingStartedMessage(msg: VoiceConfRecordingStartedMessage) {
if (msg.recording) {
liveMeeting.meetingModel.setVoiceRecordingFilename(msg.recordStream)
outGW.send(new VoiceRecordingStarted(mProps.meetingID, mProps.recorded, msg.recordStream, msg.timestamp, mProps.voiceBridge))
MeetingStatus2x.setVoiceRecordingFilename(liveMeeting.status, msg.recordStream)
outGW.send(new VoiceRecordingStarted(props.meetingProp.intId, props.recordProp.record,
msg.recordStream, msg.timestamp, props.voiceProp.voiceConf))
} else {
liveMeeting.meetingModel.setVoiceRecordingFilename("")
outGW.send(new VoiceRecordingStopped(mProps.meetingID, mProps.recorded, msg.recordStream, msg.timestamp, mProps.voiceBridge))
MeetingStatus2x.setVoiceRecordingFilename(liveMeeting.status, "")
outGW.send(new VoiceRecordingStopped(props.meetingProp.intId, props.recordProp.record,
msg.recordStream, msg.timestamp, props.voiceProp.voiceConf))
}
}
def handleSetRecordingStatus(msg: SetRecordingStatus) {
log.info("Change recording status. meetingId=" + mProps.meetingID + " recording=" + msg.recording)
if (mProps.allowStartStopRecording && liveMeeting.meetingModel.isRecording() != msg.recording) {
log.info("Change recording status. meetingId=" + props.meetingProp.intId + " recording=" + msg.recording)
if (props.recordProp.allowStartStopRecording &&
MeetingStatus2x.isRecording(liveMeeting.status) != msg.recording) {
if (msg.recording) {
liveMeeting.meetingModel.recordingStarted()
MeetingStatus2x.recordingStarted(liveMeeting.status)
} else {
liveMeeting.meetingModel.recordingStopped()
MeetingStatus2x.recordingStopped(liveMeeting.status)
}
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, msg.userId, msg.recording))
outGW.send(new RecordingStatusChanged(props.meetingProp.intId, props.recordProp.record, msg.userId, msg.recording))
}
}
// WebRTC Desktop Sharing
def handleDeskShareStartedRequest(msg: DeskShareStartedRequest): Unit = {
log.info("handleDeskShareStartedRequest: dsStarted=" + liveMeeting.meetingModel.getDeskShareStarted())
log.info("handleDeskShareStartedRequest: dsStarted=" + MeetingStatus2x.getDeskShareStarted(liveMeeting.status))
if (!liveMeeting.meetingModel.getDeskShareStarted()) {
if (!MeetingStatus2x.getDeskShareStarted(liveMeeting.status)) {
val timestamp = System.currentTimeMillis().toString
val streamPath = "rtmp://" + mProps.red5DeskShareIP + "/" + mProps.red5DeskShareApp +
"/" + mProps.meetingID + "/" + mProps.meetingID + "-" + timestamp
val streamPath = "rtmp://" + props.screenshareProps.red5ScreenshareIp + "/" + props.screenshareProps.red5ScreenshareApp +
"/" + props.meetingProp.intId + "/" + props.meetingProp.intId + "-" + timestamp
log.info("handleDeskShareStartedRequest: streamPath=" + streamPath)
// Tell FreeSwitch to broadcast to RTMP
outGW.send(new DeskShareStartRTMPBroadcast(msg.conferenceName, streamPath))
liveMeeting.meetingModel.setDeskShareStarted(true)
MeetingStatus2x.setDeskShareStarted(liveMeeting.status, true)
}
}
def handleDeskShareStoppedRequest(msg: DeskShareStoppedRequest): Unit = {
log.info("handleDeskShareStoppedRequest: dsStarted=" + liveMeeting.meetingModel.getDeskShareStarted() +
" URL:" + liveMeeting.meetingModel.getRTMPBroadcastingUrl())
log.info("handleDeskShareStoppedRequest: dsStarted=" +
MeetingStatus2x.getDeskShareStarted(liveMeeting.status) +
" URL:" + MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status))
// Tell FreeSwitch to stop broadcasting to RTMP
outGW.send(new DeskShareStopRTMPBroadcast(msg.conferenceName, liveMeeting.meetingModel.getRTMPBroadcastingUrl()))
outGW.send(new DeskShareStopRTMPBroadcast(msg.conferenceName,
MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status)))
liveMeeting.meetingModel.setDeskShareStarted(false)
MeetingStatus2x.setDeskShareStarted(liveMeeting.status, false)
}
def handleDeskShareRTMPBroadcastStartedRequest(msg: DeskShareRTMPBroadcastStartedRequest): Unit = {
log.info("handleDeskShareRTMPBroadcastStartedRequest: isBroadcastingRTMP=" + liveMeeting.meetingModel.isBroadcastingRTMP() +
" URL:" + liveMeeting.meetingModel.getRTMPBroadcastingUrl())
log.info("handleDeskShareRTMPBroadcastStartedRequest: isBroadcastingRTMP=" +
MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status) +
" URL:" + MeetingStatus2x.getRTMPBroadcastingUrl(liveMeeting.status))
// only valid if not broadcasting yet
if (!liveMeeting.meetingModel.isBroadcastingRTMP()) {
liveMeeting.meetingModel.setRTMPBroadcastingUrl(msg.streamname)
liveMeeting.meetingModel.broadcastingRTMPStarted()
liveMeeting.meetingModel.setDesktopShareVideoWidth(msg.videoWidth)
liveMeeting.meetingModel.setDesktopShareVideoHeight(msg.videoHeight)
if (!MeetingStatus2x.isBroadcastingRTMP(liveMeeting.status)) {
MeetingStatus2x.setRTMPBroadcastingUrl(liveMeeting.status, msg.streamname)
MeetingStatus2x.broadcastingRTMPStarted(liveMeeting.status)
MeetingStatus2x.setDesktopShareVideoWidth(liveMeeting.status, msg.videoWidth)
MeetingStatus2x.setDesktopShareVideoHeight(liveMeeting.status, msg.videoHeight)
log.info("START broadcast ALLOWED when isBroadcastingRTMP=false")
// Notify viewers in the meeting that there's an rtmp stream to view
outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, msg.streamname, msg.videoWidth, msg.videoHeight, true))
outGW.send(new DeskShareNotifyViewersRTMP(props.meetingProp.intId, msg.streamname, msg.videoWidth, msg.videoHeight, true))
} else {
log.info("START broadcast NOT ALLOWED when isBroadcastingRTMP=true")
}
@ -308,10 +383,11 @@ class MeetingActor(val mProps: MeetingProperties,
}
def monitorNumberOfWebUsers() {
if (Users.numWebUsers(liveMeeting.users) == 0 && liveMeeting.meetingModel.lastWebUserLeftOn > 0) {
if (liveMeeting.timeNowInMinutes - liveMeeting.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))
if (Users.numWebUsers(liveMeeting.users) == 0 &&
MeetingStatus2x.lastWebUserLeftOn(liveMeeting.status) > 0) {
if (liveMeeting.timeNowInMinutes - MeetingStatus2x.lastWebUserLeftOn(liveMeeting.status) > 2) {
log.info("Empty meeting. Ejecting all users from voice. meetingId={}", props.meetingProp.intId)
outGW.send(new EjectAllVoiceUsers(props.meetingProp.intId, props.recordProp.record, props.voiceProp.voiceConf))
}
}
}
@ -319,23 +395,24 @@ class MeetingActor(val mProps: MeetingProperties,
def monitorNumberOfUsers() {
val hasUsers = Users.numUsers(liveMeeting.users) != 0
// TODO: We could use a better control over this message to send it just when it really matters :)
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, UpdateMeetingExpireMonitor(mProps.meetingID, hasUsers)))
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, UpdateMeetingExpireMonitor(props.meetingProp.intId, hasUsers)))
}
def handleSendTimeRemainingUpdate(msg: SendTimeRemainingUpdate) {
if (mProps.duration > 0) {
val endMeetingTime = liveMeeting.meetingModel.startedOn + (mProps.duration * 60)
if (props.durationProps.duration > 0) {
val endMeetingTime = MeetingStatus2x.startedOn(liveMeeting.status) + (props.durationProps.duration * 60)
val timeRemaining = endMeetingTime - liveMeeting.timeNowInSeconds
outGW.send(new MeetingTimeRemainingUpdate(mProps.meetingID, mProps.recorded, timeRemaining.toInt))
outGW.send(new MeetingTimeRemainingUpdate(props.meetingProp.intId, props.recordProp.record, timeRemaining.toInt))
}
if (!mProps.isBreakout && liveMeeting.breakoutModel.getRooms().length > 0) {
if (!props.meetingProp.isBreakout && liveMeeting.breakoutModel.getRooms().length > 0) {
val room = liveMeeting.breakoutModel.getRooms()(0);
val endMeetingTime = liveMeeting.meetingModel.breakoutRoomsStartedOn + (liveMeeting.meetingModel.breakoutRoomsdurationInMinutes * 60)
val endMeetingTime = MeetingStatus2x.breakoutRoomsStartedOn(liveMeeting.status) +
(MeetingStatus2x.breakoutRoomsdurationInMinutes(liveMeeting.status) * 60)
val timeRemaining = endMeetingTime - liveMeeting.timeNowInSeconds
outGW.send(new BreakoutRoomsTimeRemainingUpdateOutMessage(mProps.meetingID, mProps.recorded, timeRemaining.toInt))
} else if (liveMeeting.meetingModel.breakoutRoomsStartedOn != 0) {
liveMeeting.meetingModel.breakoutRoomsdurationInMinutes = 0;
liveMeeting.meetingModel.breakoutRoomsStartedOn = 0;
outGW.send(new BreakoutRoomsTimeRemainingUpdateOutMessage(props.meetingProp.intId, props.recordProp.record, timeRemaining.toInt))
} else if (MeetingStatus2x.breakoutRoomsStartedOn(liveMeeting.status) != 0) {
MeetingStatus2x.breakoutRoomsdurationInMinutes(liveMeeting.status, 0)
MeetingStatus2x.breakoutRoomsStartedOn(liveMeeting.status, 0)
}
}
@ -344,29 +421,32 @@ class MeetingActor(val mProps: MeetingProperties,
}
def handleGetRecordingStatus(msg: GetRecordingStatus) {
outGW.send(new GetRecordingStatusReply(mProps.meetingID, mProps.recorded, msg.userId, liveMeeting.meetingModel.isRecording().booleanValue()))
outGW.send(new GetRecordingStatusReply(props.meetingProp.intId, props.recordProp.record,
msg.userId, MeetingStatus2x.isRecording(liveMeeting.status).booleanValue()))
}
def startRecordingIfAutoStart() {
if (mProps.recorded && !liveMeeting.meetingModel.isRecording() &&
mProps.autoStartRecording && Users.numWebUsers(liveMeeting.users) == 1) {
log.info("Auto start recording. meetingId={}", mProps.meetingID)
liveMeeting.meetingModel.recordingStarted()
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", liveMeeting.meetingModel.isRecording()))
if (props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) &&
props.recordProp.autoStartRecording && Users.numWebUsers(liveMeeting.users) == 1) {
log.info("Auto start recording. meetingId={}", props.meetingProp.intId)
MeetingStatus2x.recordingStarted(liveMeeting.status)
outGW.send(new RecordingStatusChanged(props.meetingProp.intId, props.recordProp.record,
"system", MeetingStatus2x.isRecording(liveMeeting.status)))
}
}
def stopAutoStartedRecording() {
if (mProps.recorded && liveMeeting.meetingModel.isRecording() &&
mProps.autoStartRecording && Users.numWebUsers(liveMeeting.users) == 0) {
log.info("Last web user left. Auto stopping recording. meetingId={}", mProps.meetingID)
liveMeeting.meetingModel.recordingStopped()
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", liveMeeting.meetingModel.isRecording()))
if (props.recordProp.record && MeetingStatus2x.isRecording(liveMeeting.status) &&
props.recordProp.autoStartRecording && Users.numWebUsers(liveMeeting.users) == 0) {
log.info("Last web user left. Auto stopping recording. meetingId={}", props.meetingProp.intId)
MeetingStatus2x.recordingStopped(liveMeeting.status)
outGW.send(new RecordingStatusChanged(props.meetingProp.intId, props.recordProp.record,
"system", MeetingStatus2x.isRecording(liveMeeting.status)))
}
}
def sendMeetingHasEnded(userId: String) {
outGW.send(new MeetingHasEnded(mProps.meetingID, userId))
outGW.send(new DisconnectUser(mProps.meetingID, userId))
outGW.send(new MeetingHasEnded(props.meetingProp.intId, userId))
outGW.send(new DisconnectUser(props.meetingProp.intId, userId))
}
}

View File

@ -1,29 +1,33 @@
package org.bigbluebutton.core.running
import java.io.{ PrintWriter, StringWriter }
import akka.actor.Actor
import akka.actor.ActorLogging
import akka.actor.Props
import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy.Resume
import scala.concurrent.duration._
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.core.{ MeetingProperties, OutMessageGateway }
import org.bigbluebutton.common2.domain.DefaultProps
import org.bigbluebutton.core.{ OutMessageGateway }
import org.bigbluebutton.core.api._
import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus }
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.{ Deadline, FiniteDuration }
object MeetingActorInternal {
def props(mProps: MeetingProperties,
def props(props: DefaultProps,
eventBus: IncomingEventBus,
outGW: OutMessageGateway): Props =
Props(classOf[MeetingActorInternal], mProps, eventBus, outGW)
Props(classOf[MeetingActorInternal], props, eventBus, outGW)
}
// This actor is an internal audit actor for each meeting actor that
// periodically sends messages to the meeting actor
class MeetingActorInternal(val mProps: MeetingProperties,
class MeetingActorInternal(val props: DefaultProps,
val eventBus: IncomingEventBus, val outGW: OutMessageGateway)
extends Actor with ActorLogging with SystemConfiguration {
@ -38,7 +42,7 @@ class MeetingActorInternal(val mProps: MeetingProperties,
}
private def getInactivityDeadline(): Int = {
val time = getMetadata(Metadata.INACTIVITY_DEADLINE, mProps.metadata) match {
val time = getMetadata(Metadata.INACTIVITY_DEADLINE, props.metadataProp.metadata) match {
case Some(result) => result.asInstanceOf[Int]
case None => inactivityDeadline
}
@ -47,7 +51,7 @@ class MeetingActorInternal(val mProps: MeetingProperties,
}
private def getInactivityTimeLeft(): Int = {
val time = getMetadata(Metadata.INACTIVITY_TIMELEFT, mProps.metadata) match {
val time = getMetadata(Metadata.INACTIVITY_TIMELEFT, props.metadataProp.metadata) match {
case Some(result) => result.asInstanceOf[Int]
case None => inactivityTimeLeft
}
@ -74,7 +78,7 @@ class MeetingActorInternal(val mProps: MeetingProperties,
private var inactivity = InactivityDeadline.fromNow
private var inactivityWarning: Deadline = null
private val ExpireMeetingDuration = FiniteDuration(mProps.duration, "minutes")
private val ExpireMeetingDuration = FiniteDuration(props.durationProps.duration, "minutes")
private val ExpireMeetingNeverJoined = FiniteDuration(getExpireNeverJoined(), "seconds")
private val ExpireMeetingLastUserLeft = FiniteDuration(getExpireLastUserLeft(), "seconds")
private var meetingExpire = ExpireMeetingNeverJoined.fromNow
@ -84,13 +88,14 @@ class MeetingActorInternal(val mProps: MeetingProperties,
context.system.scheduler.schedule(5 seconds, MonitorFrequency, self, "Monitor")
// Query to get voice conference users
outGW.send(new GetUsersInVoiceConference(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
outGW.send(new GetUsersInVoiceConference(props.meetingProp.intId, props.recordProp.record,
props.voiceProp.voiceConf))
if (mProps.isBreakout) {
if (props.meetingProp.isBreakout) {
// This is a breakout room. Inform our parent meeting that we have been successfully created.
eventBus.publish(BigBlueButtonEvent(
mProps.parentMeetingID,
BreakoutRoomCreated(mProps.parentMeetingID, mProps.meetingID)))
props.breakoutProps.parentId,
BreakoutRoomCreated(props.breakoutProps.parentId, props.meetingProp.intId)))
}
def receive = {
@ -112,27 +117,27 @@ class MeetingActorInternal(val mProps: MeetingProperties,
}
def handleMonitorNumberOfWebUsers() {
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, MonitorNumberOfUsers(mProps.meetingID)))
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, MonitorNumberOfUsers(props.meetingProp.intId)))
// Trigger updating users of time remaining on meeting.
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, SendTimeRemainingUpdate(mProps.meetingID)))
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendTimeRemainingUpdate(props.meetingProp.intId)))
if (mProps.isBreakout) {
if (props.meetingProp.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)))
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendBreakoutUsersUpdate(props.meetingProp.intId)))
}
}
private def handleMonitorActivity() {
if (inactivity.isOverdue() && inactivityWarning != null && inactivityWarning.isOverdue()) {
log.info("Closing meeting {} due to inactivity for {} seconds", mProps.meetingID, InactivityDeadline.toSeconds)
log.info("Closing meeting {} due to inactivity for {} seconds", props.meetingProp.intId, InactivityDeadline.toSeconds)
updateInactivityMonitors()
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, EndMeeting(mProps.meetingID)))
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, EndMeeting(props.meetingProp.intId)))
// Or else make sure to send only one warning message
} else if (inactivity.isOverdue() && inactivityWarning == null) {
log.info("Sending inactivity warning to meeting {}", mProps.meetingID)
outGW.send(new InactivityWarning(mProps.meetingID, InactivityTimeLeft.toSeconds))
log.info("Sending inactivity warning to meeting {}", props.meetingProp.intId)
outGW.send(new InactivityWarning(props.meetingProp.intId, InactivityTimeLeft.toSeconds))
// We add 5 seconds so clients will have enough time to process the message
inactivityWarning = (InactivityTimeLeft + (5 seconds)).fromNow
}
@ -141,14 +146,14 @@ class MeetingActorInternal(val mProps: MeetingProperties,
private def handleMonitorExpiration() {
if (meetingExpire != null && meetingExpire.isOverdue()) {
// User related meeting expiration methods
log.debug("Meeting {} expired. No users", mProps.meetingID)
log.debug("Meeting {} expired. No users", props.meetingProp.intId)
meetingExpire = null
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, EndMeeting(mProps.meetingID)))
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, EndMeeting(props.meetingProp.intId)))
} else if (meetingDuration != null && meetingDuration.isOverdue()) {
// Default meeting duration
meetingDuration = null
log.debug("Meeting {} expired. Reached it's fixed duration of {}", mProps.meetingID, ExpireMeetingDuration.toString())
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, EndMeeting(mProps.meetingID)))
log.debug("Meeting {} expired. Reached it's fixed duration of {}", props.meetingProp.intId, ExpireMeetingDuration.toString())
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, EndMeeting(props.meetingProp.intId)))
}
}
@ -156,13 +161,13 @@ class MeetingActorInternal(val mProps: MeetingProperties,
if (msg.hasUser) {
if (meetingExpire != null) {
// User joined. Forget about this expiration for now
log.debug("Meeting has users. Stopping expiration for meeting {}", mProps.meetingID)
log.debug("Meeting has users. Stopping expiration for meeting {}", props.meetingProp.intId)
meetingExpire = null
}
} else {
if (meetingExpire == null) {
// User list is empty. Start this meeting expiration method
log.debug("Meeting has no users. Starting {} expiration for meeting {}", ExpireMeetingLastUserLeft.toString(), mProps.meetingID)
log.debug("Meeting has no users. Starting {} expiration for meeting {}", ExpireMeetingLastUserLeft.toString(), props.meetingProp.intId)
meetingExpire = ExpireMeetingLastUserLeft.fromNow
}
}
@ -175,16 +180,16 @@ class MeetingActorInternal(val mProps: MeetingProperties,
private def notifyActivity() {
if (inactivityWarning != null) {
outGW.send(new MeetingIsActive(mProps.meetingID))
outGW.send(new MeetingIsActive(props.meetingProp.intId))
}
updateInactivityMonitors()
}
private def handleActivityResponse(msg: ActivityResponse) {
log.info("User endorsed that meeting {} is active", mProps.meetingID)
log.info("User endorsed that meeting {} is active", props.meetingProp.intId)
updateInactivityMonitors()
outGW.send(new MeetingIsActive(mProps.meetingID))
outGW.send(new MeetingIsActive(props.meetingProp.intId))
}
private def isMeetingActivity(msg: Object): Boolean = {

View File

@ -1,25 +1,24 @@
package org.bigbluebutton.core.running
import akka.actor.ActorRef
import akka.actor.ActorContext
import org.bigbluebutton.common2.domain.{ DefaultProps, Meeting2x }
import org.bigbluebutton.core.apps._
import org.bigbluebutton.core.bus._
import org.bigbluebutton.core.models.{ RegisteredUsers, Users }
import org.bigbluebutton.core.{ MeetingModel, MeetingProperties, OutMessageGateway }
import org.bigbluebutton.core.{ OutMessageGateway }
import org.bigbluebutton.core2.MeetingStatus2x
object RunningMeeting {
def apply(mProps: MeetingProperties, outGW: OutMessageGateway,
def apply(props: DefaultProps, outGW: OutMessageGateway,
eventBus: IncomingEventBus)(implicit context: ActorContext) =
new RunningMeeting(mProps, outGW, eventBus)(context)
new RunningMeeting(props, outGW, eventBus)(context)
}
class RunningMeeting(val mProps: MeetingProperties, val outGW: OutMessageGateway,
class RunningMeeting(val props: DefaultProps, val outGW: OutMessageGateway,
val eventBus: IncomingEventBus)(implicit val context: ActorContext) {
val chatModel = new ChatModel()
val layoutModel = new LayoutModel()
val meetingModel = new MeetingModel()
val usersModel = new UsersModel()
val pollModel = new PollModel()
val wbModel = new WhiteboardModel()
val presModel = new PresentationModel()
@ -28,15 +27,16 @@ class RunningMeeting(val mProps: MeetingProperties, val outGW: OutMessageGateway
val notesModel = new SharedNotesModel()
val users = new Users
val registeredUsers = new RegisteredUsers
val meetingStatux2x = new MeetingStatus2x
meetingModel.setGuestPolicy(mProps.guestPolicy)
// meetingModel.setGuestPolicy(props.usersProp.guestPolicy)
// We extract the meeting handlers into this class so it is
// easy to test.
val liveMeeting = new LiveMeeting(mProps,
chatModel, layoutModel, meetingModel, usersModel, users, registeredUsers, pollModel,
val liveMeeting = new LiveMeeting(props, meetingStatux2x, chatModel, layoutModel,
users, registeredUsers, pollModel,
wbModel, presModel, breakoutModel, captionModel, notesModel)
val actorRef = context.actorOf(MeetingActor.props(mProps, eventBus, outGW, liveMeeting), mProps.meetingID)
val actorRef = context.actorOf(MeetingActor.props(props, eventBus, outGW, liveMeeting), props.meetingProp.intId)
}

View File

@ -0,0 +1,26 @@
package org.bigbluebutton.core2
import akka.actor.{ Actor, ActorLogging, Props }
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.common2.messages.BbbCommonEnvCoreMsg
import org.bigbluebutton.common2.util.JsonUtil
import org.bigbluebutton.core.MessageSender
import org.bigbluebutton.core.bus.BbbOutMessage
object FromAkkaAppsMsgSenderActor {
def props(msgSender: MessageSender): Props = Props(classOf[FromAkkaAppsMsgSenderActor], msgSender)
}
class FromAkkaAppsMsgSenderActor(msgSender: MessageSender) extends Actor with ActorLogging with SystemConfiguration {
def receive = {
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
case _ => println("************* FromAkkaAppsMsgSenderActor Cannot handle message ")
}
def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
val json = JsonUtil.toJson(msg)
println("****** Publishing " + json)
msgSender.send(fromAkkaAppsRedisChannel, json)
}
}

View File

@ -0,0 +1,339 @@
package org.bigbluebutton.core2
import java.util.concurrent.TimeUnit
import org.bigbluebutton.core.MeetingExtensionProp
import org.bigbluebutton.core.api.{ GuestPolicy, Permissions, Presenter }
object MeetingStatus2x {
def setCurrentPresenterInfo(status: MeetingStatus2x, pres: Presenter) {
status.currentPresenter = pres
}
def getCurrentPresenterInfo(status: MeetingStatus2x): Presenter = {
status.currentPresenter
}
def addGlobalAudioConnection(status: MeetingStatus2x, userID: String): Boolean = {
status.globalAudioConnectionCounter.get(userID) match {
case Some(vc) => {
status.globalAudioConnectionCounter += userID -> (vc + 1)
false
}
case None => {
status.globalAudioConnectionCounter += userID -> 1
true
}
}
}
def removeGlobalAudioConnection(status: MeetingStatus2x, userID: String): Boolean = {
status.globalAudioConnectionCounter.get(userID) match {
case Some(vc) => {
if (vc == 1) {
status.globalAudioConnectionCounter -= userID
true
} else {
status.globalAudioConnectionCounter += userID -> (vc - 1)
false
}
}
case None => {
false
}
}
}
def startRecordingVoice(status: MeetingStatus2x): Boolean = {
status.recordingVoice = true
status.recordingVoice
}
def stopRecordingVoice(status: MeetingStatus2x): Boolean = {
status.recordingVoice = false
status.recordingVoice
}
def isVoiceRecording(status: MeetingStatus2x): Boolean = {
status.recordingVoice
}
def resetDesktopSharingParams(status: MeetingStatus2x) = {
status.broadcastingRTMP = false
status.deskShareStarted = false
status.rtmpBroadcastingUrl = ""
status.desktopShareVideoWidth = 0
status.desktopShareVideoHeight = 0
}
def getDeskShareStarted(status: MeetingStatus2x): Boolean = {
return status.deskShareStarted
}
def setDeskShareStarted(status: MeetingStatus2x, b: Boolean) {
status.deskShareStarted = b
}
def setDesktopShareVideoWidth(status: MeetingStatus2x, videoWidth: Int) {
status.desktopShareVideoWidth = videoWidth
}
def setDesktopShareVideoHeight(status: MeetingStatus2x, videoHeight: Int) {
status.desktopShareVideoHeight = videoHeight
}
def getDesktopShareVideoWidth(status: MeetingStatus2x): Int = {
status.desktopShareVideoWidth
}
def getDesktopShareVideoHeight(status: MeetingStatus2x): Int = {
status.desktopShareVideoHeight
}
def broadcastingRTMPStarted(status: MeetingStatus2x) {
status.broadcastingRTMP = true
}
def isBroadcastingRTMP(status: MeetingStatus2x): Boolean = {
status.broadcastingRTMP
}
def broadcastingRTMPStopped(status: MeetingStatus2x) {
status.broadcastingRTMP = false
}
def setRTMPBroadcastingUrl(status: MeetingStatus2x, path: String) {
status.rtmpBroadcastingUrl = path
}
def getRTMPBroadcastingUrl(status: MeetingStatus2x): String = {
status.rtmpBroadcastingUrl
}
def isExtensionAllowed(status: MeetingStatus2x): Boolean = status.extension.numExtensions < status.extension.maxExtensions
def incNumExtension(status: MeetingStatus2x): Int = {
if (status.extension.numExtensions < status.extension.maxExtensions) {
status.extension = status.extension.copy(numExtensions = status.extension.numExtensions + 1); status.extension.numExtensions
}
status.extension.numExtensions
}
def notice15MinutesSent(status: MeetingStatus2x) = status.extension = status.extension.copy(sent15MinNotice = true)
def notice10MinutesSent(status: MeetingStatus2x) = status.extension = status.extension.copy(sent10MinNotice = true)
def notice5MinutesSent(status: MeetingStatus2x) = status.extension = status.extension.copy(sent5MinNotice = true)
def getMeetingExtensionProp(status: MeetingStatus2x): MeetingExtensionProp = status.extension
def muteMeeting(status: MeetingStatus2x) = status.meetingMuted = true
def unmuteMeeting(status: MeetingStatus2x) = status.meetingMuted = false
def isMeetingMuted(status: MeetingStatus2x): Boolean = status.meetingMuted
def recordingStarted(status: MeetingStatus2x) = status.recording = true
def recordingStopped(status: MeetingStatus2x) = status.recording = false
def isRecording(status: MeetingStatus2x): Boolean = status.recording
def lastWebUserLeft(status: MeetingStatus2x) = status.lastWebUserLeftOnTimestamp = MeetingStatus2x.timeNowInMinutes
def lastWebUserLeftOn(status: MeetingStatus2x): Long = status.lastWebUserLeftOnTimestamp
def resetLastWebUserLeftOn(status: MeetingStatus2x) = status.lastWebUserLeftOnTimestamp = 0
def setVoiceRecordingFilename(status: MeetingStatus2x, path: String) = status.voiceRecordingFilename = path
def getVoiceRecordingFilename(status: MeetingStatus2x): String = status.voiceRecordingFilename
def permisionsInitialized(status: MeetingStatus2x): Boolean = status.permissionsInited
def initializePermissions(status: MeetingStatus2x) = status.permissionsInited = true
def audioSettingsInitialized(status: MeetingStatus2x): Boolean = status.audioSettingsInited
def initializeAudioSettings(status: MeetingStatus2x) = status.audioSettingsInited = true
def permissionsEqual(status: MeetingStatus2x, other: Permissions): Boolean = status.permissions == other
def lockLayout(status: MeetingStatus2x, lock: Boolean) = status.permissions = status.permissions.copy(lockedLayout = lock)
def getPermissions(status: MeetingStatus2x): Permissions = status.permissions
def setPermissions(status: MeetingStatus2x, p: Permissions) = status.permissions = p
def meetingHasEnded(status: MeetingStatus2x) = status.meetingEnded = true
def hasMeetingEnded(status: MeetingStatus2x): Boolean = status.meetingEnded
def timeNowInMinutes(status: MeetingStatus2x): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
def timeNowInSeconds(status: MeetingStatus2x): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
def getGuestPolicy(status: MeetingStatus2x): GuestPolicy.GuestPolicy = status.guestPolicy
def setGuestPolicy(status: MeetingStatus2x, policy: GuestPolicy.GuestPolicy) = status.guestPolicy = policy
def getGuestPolicySetBy(status: MeetingStatus2x): String = status.guestPolicySetBy
def setGuestPolicySetBy(status: MeetingStatus2x, user: String) = status.guestPolicySetBy = user
def startedOn(status: MeetingStatus2x): Long = status.startedOn
def breakoutRoomsStartedOn(status: MeetingStatus2x) = status.breakoutRoomsStartedOn
def breakoutRoomsStartedOn(status: MeetingStatus2x, startedOn: Long) = status.breakoutRoomsStartedOn = startedOn
def breakoutRoomsdurationInMinutes(status: MeetingStatus2x) = status.breakoutRoomsdurationInMinutes
def breakoutRoomsdurationInMinutes(status: MeetingStatus2x, duration: Int) = status.breakoutRoomsdurationInMinutes = duration
def timeNowInMinutes(): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
def timeNowInSeconds(): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
}
class MeetingStatus2x {
private var globalAudioConnectionCounter = new collection.immutable.HashMap[String, Integer]
private var recordingVoice = false
private var currentPresenter = new Presenter("system", "system", "system")
private var audioSettingsInited = false
private var permissionsInited = false
private var permissions = new Permissions()
private var recording = false
private var broadcastingRTMP = false
private var muted = false
private var meetingEnded = false
private var meetingMuted = false
private var guestPolicy = GuestPolicy.ASK_MODERATOR
private var guestPolicySetBy: String = null
private var hasLastWebUserLeft = false
private var lastWebUserLeftOnTimestamp: Long = 0
private var voiceRecordingFilename: String = ""
private var rtmpBroadcastingUrl: String = ""
private var deskShareStarted = false
private var desktopShareVideoWidth = 0
private var desktopShareVideoHeight = 0
private var extension = new MeetingExtensionProp
private val startedOn = MeetingStatus2x.timeNowInSeconds;
private var breakoutRoomsStartedOn: Long = 0
private var breakoutRoomsdurationInMinutes: Int = 0
private def setCurrentPresenterInfo(pres: Presenter) {
currentPresenter = pres
}
private def getCurrentPresenterInfo(): Presenter = {
currentPresenter
}
private def addGlobalAudioConnection(userID: String): Boolean = {
globalAudioConnectionCounter.get(userID) match {
case Some(vc) => {
globalAudioConnectionCounter += userID -> (vc + 1)
false
}
case None => {
globalAudioConnectionCounter += userID -> 1
true
}
}
}
private def removeGlobalAudioConnection(userID: String): Boolean = {
globalAudioConnectionCounter.get(userID) match {
case Some(vc) => {
if (vc == 1) {
globalAudioConnectionCounter -= userID
true
} else {
globalAudioConnectionCounter += userID -> (vc - 1)
false
}
}
case None => {
false
}
}
}
private def startRecordingVoice() {
recordingVoice = true
}
private def stopRecordingVoice() {
recordingVoice = false
}
private def isVoiceRecording: Boolean = {
recordingVoice
}
private def resetDesktopSharingParams() = {
broadcastingRTMP = false
deskShareStarted = false
rtmpBroadcastingUrl = ""
desktopShareVideoWidth = 0
desktopShareVideoHeight = 0
}
private def getDeskShareStarted(): Boolean = {
return deskShareStarted
}
private def setDeskShareStarted(b: Boolean) {
deskShareStarted = b
}
private def setDesktopShareVideoWidth(videoWidth: Int) {
desktopShareVideoWidth = videoWidth
}
private def setDesktopShareVideoHeight(videoHeight: Int) {
desktopShareVideoHeight = videoHeight
}
private def getDesktopShareVideoWidth(): Int = {
desktopShareVideoWidth
}
private def getDesktopShareVideoHeight(): Int = {
desktopShareVideoHeight
}
private def broadcastingRTMPStarted() {
broadcastingRTMP = true
}
private def isBroadcastingRTMP(): Boolean = {
broadcastingRTMP
}
private def broadcastingRTMPStopped() {
broadcastingRTMP = false
}
private def setRTMPBroadcastingUrl(path: String) {
rtmpBroadcastingUrl = path
}
private def getRTMPBroadcastingUrl(): String = {
rtmpBroadcastingUrl
}
private def isExtensionAllowed(): Boolean = extension.numExtensions < extension.maxExtensions
private def incNumExtension(): Int = {
if (extension.numExtensions < extension.maxExtensions) {
extension = extension.copy(numExtensions = extension.numExtensions + 1); extension.numExtensions
}
extension.numExtensions
}
private def notice15MinutesSent() = extension = extension.copy(sent15MinNotice = true)
private def notice10MinutesSent() = extension = extension.copy(sent10MinNotice = true)
private def notice5MinutesSent() = extension = extension.copy(sent5MinNotice = true)
private def getMeetingExtensionProp(): MeetingExtensionProp = extension
private def muteMeeting() = meetingMuted = true
private def unmuteMeeting() = meetingMuted = false
private def isMeetingMuted(): Boolean = meetingMuted
private def recordingStarted() = recording = true
private def recordingStopped() = recording = false
private def isRecording(): Boolean = recording
private def lastWebUserLeft() = lastWebUserLeftOnTimestamp = MeetingStatus2x.timeNowInMinutes
private def lastWebUserLeftOn(): Long = lastWebUserLeftOnTimestamp
private def resetLastWebUserLeftOn() = lastWebUserLeftOnTimestamp = 0
private def setVoiceRecordingFilename(path: String) = voiceRecordingFilename = path
private def getVoiceRecordingFilename(): String = voiceRecordingFilename
private def permisionsInitialized(): Boolean = permissionsInited
private def initializePermissions() = permissionsInited = true
private def audioSettingsInitialized(): Boolean = audioSettingsInited
private def initializeAudioSettings() = audioSettingsInited = true
private def permissionsEqual(other: Permissions): Boolean = permissions == other
private def lockLayout(lock: Boolean) = permissions = permissions.copy(lockedLayout = lock)
private def getPermissions(): Permissions = permissions
private def setPermissions(p: Permissions) = permissions = p
private def meetingHasEnded() = meetingEnded = true
private def hasMeetingEnded(): Boolean = meetingEnded
private def getGuestPolicy(): GuestPolicy.GuestPolicy = guestPolicy
private def setGuestPolicy(policy: GuestPolicy.GuestPolicy) = guestPolicy = policy
private def getGuestPolicySetBy(): String = guestPolicySetBy
private def setGuestPolicySetBy(user: String) = guestPolicySetBy = user
}

View File

@ -0,0 +1,28 @@
package org.bigbluebutton.core2
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.common2.messages._
import org.bigbluebutton.core.bus.{ BbbMsgEvent, BbbMsgRouterEventBus }
trait ReceivedMessageRouter extends SystemConfiguration {
val eventBus: BbbMsgRouterEventBus
def publish(msg: BbbMsgEvent): Unit = {
eventBus.publish(msg)
}
def send(envelope: BbbCoreEnvelope, msg: CreateMeetingReqMsg): Unit = {
val event = BbbMsgEvent(meetingManagerChannel, BbbCommonEnvCoreMsg(envelope, msg))
publish(event)
}
def send(envelope: BbbCoreEnvelope, msg: ValidateAuthTokenReqMsg): Unit = {
val event = BbbMsgEvent(msg.header.meetingId, BbbCommonEnvCoreMsg(envelope, msg))
publish(event)
}
def send(envelope: BbbCoreEnvelope, msg: RegisterUserReqMsg): Unit = {
val event = BbbMsgEvent(meetingManagerChannel, BbbCommonEnvCoreMsg(envelope, msg))
publish(event)
}
}

View File

@ -0,0 +1,48 @@
package org.bigbluebutton.core2
import org.bigbluebutton.core.running.RunningMeeting
object RunningMeetings {
def findWithId(meetings: RunningMeetings, id: String): Option[RunningMeeting] = {
meetings.toVector.find(m => m.props.meetingProp.intId == id)
}
def add(meetings: RunningMeetings, meeting: RunningMeeting): RunningMeeting = {
meetings.save(meeting)
meeting
}
def remove(meetings: RunningMeetings, id: String): Option[RunningMeeting] = {
meetings.remove(id)
}
def numMeetings(meetings: RunningMeetings): Int = {
meetings.toVector.length
}
def meetings(meetings: RunningMeetings): Vector[RunningMeeting] = {
meetings.toVector
}
def findMeetingWithVoiceConfId(meetings: RunningMeetings, voiceConfId: String): Option[RunningMeeting] = {
meetings.toVector.find(m => { m.props.voiceProp.voiceConf == voiceConfId })
}
}
class RunningMeetings {
private var meetings = new collection.immutable.HashMap[String, RunningMeeting]
private def toVector: Vector[RunningMeeting] = meetings.values.toVector
private def save(meeting: RunningMeeting): RunningMeeting = {
meetings += meeting.props.meetingProp.intId -> meeting
meeting
}
private def remove(id: String): Option[RunningMeeting] = {
val meeting = meetings.get(id)
meeting foreach (u => meetings -= id)
meeting
}
}

View File

@ -0,0 +1,5 @@
package org.bigbluebutton.core2.message.handlers
trait CreateMeetingReqMsgHdlr {
}

View File

@ -5,32 +5,33 @@ import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy.Resume
import java.io.{ PrintWriter, StringWriter }
import java.net.InetSocketAddress
import redis.actors.RedisSubscriberActor
import redis.api.pubsub.{ PMessage, Message }
import redis.api.pubsub.{ Message, PMessage }
import scala.concurrent.duration._
import akka.actor.ActorRef
import akka.actor.actorRef2Scala
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.core.bus.{ IncomingJsonMessage, IncomingJsonMessageBus, ReceivedJsonMessage }
import org.bigbluebutton.core.pubsub.receivers.RedisMessageReceiver
import redis.api.servers.ClientSetname
object AppsRedisSubscriberActor extends SystemConfiguration {
val channels = Seq("time")
val TO_AKKA_APPS = "bbb:to-akka-apps"
val channels = Seq("time", toAkkaAppsRedisChannel)
val patterns = Seq("bigbluebutton:to-bbb-apps:*", "bigbluebutton:from-voice-conf:*")
def props(msgReceiver: RedisMessageReceiver): Props =
Props(classOf[AppsRedisSubscriberActor], msgReceiver,
def props(msgReceiver: RedisMessageReceiver, jsonMsgBus: IncomingJsonMessageBus): Props =
Props(classOf[AppsRedisSubscriberActor], msgReceiver, jsonMsgBus,
redisHost, redisPort,
channels, patterns).withDispatcher("akka.rediscala-subscriber-worker-dispatcher")
}
class AppsRedisSubscriberActor(msgReceiver: RedisMessageReceiver, redisHost: String,
class AppsRedisSubscriberActor(msgReceiver: RedisMessageReceiver, jsonMsgBus: IncomingJsonMessageBus, redisHost: String,
redisPort: Int,
channels: Seq[String] = Nil, patterns: Seq[String] = Nil)
extends RedisSubscriberActor(
new InetSocketAddress(redisHost, redisPort),
channels, patterns) {
extends RedisSubscriberActor(new InetSocketAddress(redisHost, redisPort),
channels, patterns, onConnectStatus = connected => { println(s"connected: $connected") }) with SystemConfiguration {
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case e: Exception => {
@ -47,11 +48,17 @@ class AppsRedisSubscriberActor(msgReceiver: RedisMessageReceiver, redisHost: Str
write(ClientSetname("BbbAppsAkkaSub").encodedRequest)
def onMessage(message: Message) {
log.error(s"SHOULD NOT BE RECEIVING: $message")
//log.error(s"SHOULD NOT BE RECEIVING: $message")
if (message.channel == toAkkaAppsRedisChannel) {
val receivedJsonMessage = new ReceivedJsonMessage(message.channel, message.data.utf8String)
log.debug(s"RECEIVED:\n [${receivedJsonMessage.channel}] \n ${receivedJsonMessage.data} \n")
jsonMsgBus.publish(IncomingJsonMessage(toAkkaAppsJsonChannel, receivedJsonMessage))
}
}
def onPMessage(pmessage: PMessage) {
//log.debug(s"RECEIVED:\n $pmessage \n")
msgReceiver.handleMessage(pmessage.patternMatched, pmessage.channel, pmessage.data)
//log.debug(s"RECEIVED:\n ${pmessage.data.utf8String} \n")
msgReceiver.handleMessage(pmessage.patternMatched, pmessage.channel, pmessage.data.utf8String)
}
}
}

View File

@ -1,7 +1,5 @@
package org.bigbluebutton.endpoint.redis
import akka.actor.Props
import redis.RedisClient
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import akka.actor.ActorSystem
@ -14,6 +12,6 @@ class KeepAliveRedisPublisher(val system: ActorSystem, sender: RedisPublisher) e
system.scheduler.schedule(2 seconds, 5 seconds) {
val msg = new BbbAppsIsAliveMessage(startedOn, System.currentTimeMillis())
sender.publish("bigbluebutton:from-bbb-apps:keepalive", msg.toJson())
// sender.publish("bigbluebutton:from-bbb-apps:keepalive", msg.toJson())
}
}

View File

@ -1,13 +1,9 @@
package org.bigbluebutton.endpoint.redis
import akka.actor.Props
import redis.RedisClient
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import akka.actor.ActorSystem
import scala.concurrent.Await
import akka.actor.Actor
import org.bigbluebutton.SystemConfiguration
import akka.util.ByteString
class RedisPublisher(val system: ActorSystem) extends SystemConfiguration {
@ -18,8 +14,8 @@ class RedisPublisher(val system: ActorSystem) extends SystemConfiguration {
redis.clientSetname("BbbAppsAkkaPub")
def publish(channel: String, data: String) {
//println("PUBLISH TO [" + channel + "]: \n [" + data + "]")
redis.publish(channel, data)
println("PUBLISH TO [" + channel + "]: \n [" + data + "]")
redis.publish(channel, ByteString(data))
}
}

View File

@ -1,6 +1,10 @@
package org.bigbluebutton.core
import org.bigbluebutton.common2.domain._
import org.bigbluebutton.core.api.GuestPolicy
import org.bigbluebutton.core.apps._
import org.bigbluebutton.core.models.{ RegisteredUsers, Users }
import org.bigbluebutton.core2.MeetingStatus2x
trait AppsTestFixtures {
@ -21,16 +25,46 @@ trait AppsTestFixtures {
val createTime = System.currentTimeMillis
val createDate = "Oct 26, 2015"
val isBreakout = false
val red5DeskShareIP = "127.0.0.1"
val red5DeskShareApp = "red5App"
val metadata: collection.immutable.Map[String, String] = Map("foo" -> "bar", "bar" -> "baz", "baz" -> "foo")
val welcomeMsgTemplate = "Welcome message template"
val welcomeMsg = "Welcome message"
val modOnlyMessage = "Moderator only message"
val dialNumber = "613-555-1234"
val maxUsers = 25
val guestPolicy = "ALWAYS_ASK"
val red5DeskShareIPTestFixture = "127.0.0.1"
val red5DeskShareAppTestFixtures = "red5App"
val metadata: collection.immutable.Map[String, String] = Map("foo" -> "bar", "bar" -> "baz", "baz" -> "foo")
val screenshareProps = ScreenshareProps("TODO", "TODO", "TODO")
val breakoutProps = BreakoutProps(parentMeetingId, sequence, Vector())
val meetingStatux2x = new MeetingStatus2x
val chatModel = new ChatModel()
val layoutModel = new LayoutModel()
val meetingModel = new MeetingModel()
val pollModel = new PollModel()
val wbModel = new WhiteboardModel()
val presModel = new PresentationModel()
val breakoutModel = new BreakoutRoomModel()
val captionModel = new CaptionModel()
val notesModel = new SharedNotesModel()
val users = new Users
val registeredUsers = new RegisteredUsers
val meetingProp = MeetingProp(name = meetingName, extId = externalMeetingId, intId = meetingId,
isBreakout = isBreakout.booleanValue())
val durationProps = DurationProps(duration = durationInMinutes, createdTime = createTime, createdDate = createDate)
val password = PasswordProp(moderatorPass = moderatorPassword, viewerPass = viewerPassword)
val recordProp = RecordProp(record = record, autoStartRecording = autoStartRecording,
allowStartStopRecording = allowStartStopRecording)
val welcomeProp = WelcomeProp(welcomeMsgTemplate = welcomeMsgTemplate, welcomeMsg = welcomeMsg,
modOnlyMessage = modOnlyMessage)
val voiceProp = VoiceProp(telVoice = voiceConfId, voiceConf = voiceConfId, dialNumber = dialNumber)
val usersProp = UsersProp(maxUsers = maxUsers, webcamsOnlyForModerator = webcamsOnlyForModerator,
guestPolicy = guestPolicy)
val metadataProp = new MetadataProp(metadata)
val defaultProps = DefaultProps(meetingProp, breakoutProps, durationProps, password, recordProp, welcomeProp, voiceProp,
usersProp, metadataProp, screenshareProps)
val mProps = new MeetingProperties(meetingId, externalMeetingId, parentMeetingId,
meetingName, record,
voiceConfId, deskshareConfId,
durationInMinutes,
autoStartRecording, allowStartStopRecording, webcamsOnlyForModerator,
moderatorPassword, viewerPassword,
createTime, createDate, red5DeskShareIP, red5DeskShareApp,
isBreakout, sequence, metadata, GuestPolicy.ALWAYS_ACCEPT)
}

View File

@ -11,7 +11,6 @@ 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

View File

@ -2,7 +2,6 @@ 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

@ -1,23 +0,0 @@
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

@ -1,7 +1,5 @@
package org.bigbluebutton.core.apps
import collection.mutable.Stack
import org.scalatest._
import org.bigbluebutton.core.UnitSpec
class BreakoutRoomsUtilSpec extends UnitSpec {

View File

@ -1,17 +1,23 @@
package org.bigbluebutton.core.models
import org.scalatest._
import org.bigbluebutton.core.UnitSpec
import scala.collection.mutable.Stack
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.{ AppsTestFixtures, UnitSpec }
import scala.collection.immutable.List
class ChatModelTest extends UnitSpec {
class ChatModelTest extends UnitSpec with AppsTestFixtures {
val liveMeeting = new LiveMeeting(defaultProps, meetingStatux2x,
chatModel, layoutModel, users, registeredUsers, pollModel,
wbModel, presModel, breakoutModel, captionModel, notesModel)
"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)
var stack = List[Int]()
stack = stack :+ 1
stack = stack :+ 2
assert(stack.length === 2)
stack = stack.dropRight(1)
assert(stack.length === 1)
assert(stack.contains(1))
}
}

View File

@ -13,21 +13,27 @@ class DirectChatModelTest extends UnitSpec {
val between = Set("user1", "user2")
val directChats = new DirectChats()
val dc = DirectChats.create(between, directChats)
val dm2 = dc.append(dm)
// Add a message and check that there is only one message
dc.append(dm)
assert(dc.messages.length == 1)
val dm3 = dc.append(dm)
// Add another message and check that there are two messages
dc.append(dm)
assert(dc.messages.length == 2)
// Find the direct chat
val dc2 = DirectChats.find(between, directChats)
dc2 match {
case Some(directChat) => assert(directChat.messages.length == 2)
case None => fail("No direct chat found!")
}
val dm4 = dc.append(dm)
// Append a third message and make sure there are three messages
dc.append(dm)
assert(dc.messages.length == 3)
// Check that we are updating the correct direct chat in the model.
val dc3 = DirectChats.find(between, directChats)
dc3 match {
case Some(directChat) => assert(directChat.messages.length == 3)

View File

@ -0,0 +1,40 @@
package org.bigbluebutton.core.pubsub.sender
import com.fasterxml.jackson.databind.JsonDeserializer
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.common2.messages.MessageBody.CreateMeetingReqMsgBody
import org.bigbluebutton.core.{ AppsTestFixtures, UnitSpec }
import org.bigbluebutton.common2.messages._
import org.bigbluebutton.common2.util.JsonUtil
import org.bigbluebutton.core.bus.{ BbbMsgEvent, BbbMsgRouterEventBus, ReceivedJsonMessage }
import org.bigbluebutton.core.pubsub.senders.ReceivedJsonMsgDeserializer
import org.bigbluebutton.core2.ReceivedMessageRouter
import org.mockito.Mockito._
import org.scalatest.mockito.MockitoSugar
class ReceivedJsonMsgHandlerTraitTests extends UnitSpec
with AppsTestFixtures with MockitoSugar with SystemConfiguration {
class MessageRouter(val eventBus: BbbMsgRouterEventBus) extends ReceivedMessageRouter {
}
"It" should "be able to decode envelope and core message" in {
val mockEventBus = mock[BbbMsgRouterEventBus]
val classUnderTest = new MessageRouter(mockEventBus)
val routing = collection.immutable.HashMap("sender" -> "bbb-web")
val envelope = BbbCoreEnvelope(CreateMeetingReqMsg.NAME, routing)
val header = BbbCoreBaseHeader(CreateMeetingReqMsg.NAME)
val body = CreateMeetingReqMsgBody(defaultProps)
val req = CreateMeetingReqMsg(header, body)
object JsonDeserializer extends Deserializer
classUnderTest.send(envelope, req)
// Then verify the class under test used the mock object as expected
// The disconnect user shouldn't be called as user has ability to eject another user
val event = BbbMsgEvent(meetingManagerChannel, BbbCommonEnvCoreMsg(envelope, req))
verify(mockEventBus, times(1)).publish(event)
}
}

View File

@ -6,7 +6,7 @@ organization := "org.bigbluebutton"
version := "0.0.1"
scalaVersion := "2.11.6"
scalaVersion := "2.12.2"
scalacOptions ++= Seq(
"-unchecked",
@ -14,7 +14,7 @@ scalacOptions ++= Seq(
"-Xlint",
"-Ywarn-dead-code",
"-language:_",
"-target:jvm-1.7",
"-target:jvm-1.8",
"-encoding", "UTF-8"
)
@ -38,7 +38,7 @@ 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.5.1"
Seq(
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
"com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
@ -46,13 +46,13 @@ libraryDependencies ++= {
"ch.qos.logback" % "logback-classic" % "1.0.3",
"org.pegdown" % "pegdown" % "1.4.0",
"junit" % "junit" % "4.11",
"com.etaty.rediscala" %% "rediscala" % "1.4.0",
"com.github.etaty" % "rediscala_2.12" % "1.8.0",
"commons-codec" % "commons-codec" % "1.10",
"joda-time" % "joda-time" % "2.3",
"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.19-SNAPSHOT",
"org.bigbluebutton" % "bbb-common-message_2.12" % "0.0.19-SNAPSHOT",
"org.bigbluebutton" % "bbb-fsesl-client" % "0.0.4"
)}

View File

@ -1,23 +1,25 @@
package org.bigbluebutton.endpoint.redis
import akka.actor.Props
import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy.Resume
import java.io.{ PrintWriter, StringWriter }
import java.io.PrintWriter
import java.io.StringWriter
import java.net.InetSocketAddress
import redis.actors.RedisSubscriberActor
import redis.api.pubsub.{ PMessage, Message }
import scala.concurrent.duration._
import akka.actor.ActorRef
import akka.actor.actorRef2Scala
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.DurationInt
import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.freeswitch.pubsub.receivers.RedisMessageReceiver
import redis.api.servers.ClientSetname
import org.bigbluebutton.common.converters.FromJsonDecoder
import org.bigbluebutton.common.messages.PubSubPongMessage
import org.bigbluebutton.freeswitch.pubsub.receivers.RedisMessageReceiver
import akka.actor.ActorSystem
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import akka.actor.OneForOneStrategy
import akka.actor.Props
import akka.actor.SupervisorStrategy.Resume
import redis.actors.RedisSubscriberActor
import redis.api.pubsub.Message
import redis.api.pubsub.PMessage
import redis.api.servers.ClientSetname
object AppsRedisSubscriberActor extends SystemConfiguration {
@ -31,9 +33,10 @@ object AppsRedisSubscriberActor extends SystemConfiguration {
}
class AppsRedisSubscriberActor(val system: ActorSystem, msgReceiver: RedisMessageReceiver, redisHost: String,
redisPort: Int, channels: Seq[String] = Nil, patterns: Seq[String] = Nil)
redisPort: Int,
channels: Seq[String] = Nil, patterns: Seq[String] = Nil)
extends RedisSubscriberActor(new InetSocketAddress(redisHost, redisPort),
channels, patterns) {
channels, patterns, onConnectStatus = connected => { println(s"connected: $connected") }) with SystemConfiguration {
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case e: Exception => {
@ -69,7 +72,7 @@ class AppsRedisSubscriberActor(val system: ActorSystem, msgReceiver: RedisMessag
def onPMessage(pmessage: PMessage) {
// log.debug(s"pattern message received: $pmessage")
val msg = decoder.decodeMessage(pmessage.data)
val msg = decoder.decodeMessage(pmessage.data.utf8String)
if (msg != null) {
msg match {
@ -81,7 +84,7 @@ class AppsRedisSubscriberActor(val system: ActorSystem, msgReceiver: RedisMessag
case _ => // do nothing
}
} else {
msgReceiver.handleMessage(pmessage.patternMatched, pmessage.channel, pmessage.data)
msgReceiver.handleMessage(pmessage.patternMatched, pmessage.channel, pmessage.data.utf8String)
}
}

View File

@ -24,7 +24,7 @@ class RedisPublisher(val system: ActorSystem) extends SystemConfiguration {
redis.publish("bigbluebutton:to-bbb-apps:system", json)
}
system.scheduler.schedule(10 seconds, 10 seconds)(sendPingMessage())
// system.scheduler.schedule(10 seconds, 10 seconds)(sendPingMessage())
def publish(channel: String, data: String) {
//println("PUBLISH TO [" + channel + "]: \n [" + data + "]")

56
bbb-apps-common/.gitignore vendored Normal file
View File

@ -0,0 +1,56 @@
.DS_Store
._.DS_Store*
.metadata
.project
.classpath
.settings
.history
.worksheet
gen
**/*.swp
**/*~.nib
**/build/
**/*.pbxuser
**/*.perspective
**/*.perspectivev3
*.xcworkspace
*.xcuserdatad
**/target
target
*.iml
project/*.ipr
project/*.iml
project/*.iws
project/out
project/*/target
project/target
project/*/bin
project/*/build
project/*.iml
project/*/*.iml
project/.idea
project/.idea/*
.idea
.idea/*
.idea/**/*
.DS_Store
project/.DS_Store
project/*/.DS_Store
tm.out
tmlog*.log
*.tm*.epoch
out/
provisioning/.vagrant
provisioning/*/.vagrant
provisioning/*/*.known
/sbt/akka-patterns-store/
/daemon/src/build/
*.lock
log/
tmp/
build/
akka-patterns-store/
lib_managed/
.cache
bin/

139
bbb-apps-common/build.sbt Executable file
View File

@ -0,0 +1,139 @@
name := "bbb-apps-common"
organization := "org.bigbluebutton"
version := "0.0.1-SNAPSHOT"
scalaVersion := "2.12.2"
scalacOptions ++= Seq(
"-unchecked",
"-deprecation",
"-Xlint",
"-Ywarn-dead-code",
"-language:_",
"-target:jvm-1.8",
"-encoding", "UTF-8"
)
// We want to have our jar files in lib_managed dir.
// This way we'll have the right path when we import
// into eclipse.
retrieveManaged := true
testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "html", "console", "junitxml")
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/scalatest-reports")
val scalaV = "2.12.2"
val akkaVersion = "2.5.1"
val scalaTestV = "2.2.6"
// https://mvnrepository.com/artifact/org.scala-lang/scala-library
libraryDependencies += "org.scala-lang" % "scala-library" % "2.12.2"
// https://mvnrepository.com/artifact/org.scala-lang/scala-compiler
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.12.2"
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-actor_2.12
libraryDependencies += "com.typesafe.akka" % "akka-actor_2.12" % "2.5.1"
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-slf4j_2.12
libraryDependencies += "com.typesafe.akka" % "akka-slf4j_2.12" % "2.5.1"
// https://mvnrepository.com/artifact/com.github.etaty/rediscala_2.12
libraryDependencies += "com.github.etaty" % "rediscala_2.12" % "1.8.0"
libraryDependencies += "com.softwaremill.quicklens" %% "quicklens" % "1.4.8"
libraryDependencies += "org.bigbluebutton" % "bbb-common-message_2.12" % "0.0.19-SNAPSHOT"
libraryDependencies += "com.google.code.gson" % "gson" % "2.8.0"
libraryDependencies += "redis.clients" % "jedis" % "2.9.0"
// https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.5"
libraryDependencies += "commons-io" % "commons-io" % "2.4"
libraryDependencies += "org.apache.commons" % "commons-pool2" % "2.3"
libraryDependencies += "commons-io" % "commons-io" % "2.4"
libraryDependencies += "org.slf4j" % "slf4j-api" % "1.7.23" % "provided"
libraryDependencies += "junit" % "junit" % "4.12" % "test"
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
// For generating test reports
libraryDependencies += "org.pegdown" % "pegdown" % "1.6.0" % "test"
// https://mvnrepository.com/artifact/com.typesafe.akka/akka-testkit_2.12
libraryDependencies += "com.typesafe.akka" % "akka-testkit_2.12" % "2.5.1" % "test"
// https://mvnrepository.com/artifact/org.scalactic/scalactic_2.12
libraryDependencies += "org.scalactic" % "scalactic_2.12" % "3.0.3" % "test"
// https://mvnrepository.com/artifact/org.scalatest/scalatest_2.12
libraryDependencies += "org.scalatest" % "scalatest_2.12" % "3.0.3" % "test"
libraryDependencies += "org.mockito" % "mockito-core" % "2.7.22" % "test"
seq(Revolver.settings: _*)
//-----------
// Packaging
//
// Reference:
// http://xerial.org/blog/2014/03/24/sbt/
// http://www.scala-sbt.org/sbt-pgp/usage.html
// http://www.scala-sbt.org/0.13/docs/Using-Sonatype.html
// http://central.sonatype.org/pages/requirements.html
// http://central.sonatype.org/pages/releasing-the-deployment.html
//-----------
// Build pure Java lib (i.e. without scala)
// Do not append Scala versions to the generated artifacts
//crossPaths := false
// This forbids including Scala related libraries into the dependency
//autoScalaLibrary := false
/***************************
* When developing, change the version above to x.x.x-SNAPSHOT then use the file resolver to
* 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")))
// 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")
//}
// Enables publishing to maven repo
publishMavenStyle := true
publishArtifact in Test := false
pomIncludeRepository := { _ => false }
pomExtra := (
<scm>
<url>git@github.com:bigbluebutton/bigbluebutton.git</url>
<connection>scm:git:git@github.com:bigbluebutton/bigbluebutton.git</connection>
</scm>
<developers>
<developer>
<id>ritzalam</id>
<name>Richard Alam</name>
<url>http://www.bigbluebutton.org</url>
</developer>
</developers>)
licenses := Seq("LGPL-3.0" -> url("http://opensource.org/licenses/LGPL-3.0"))
homepage := Some(url("http://www.bigbluebutton.org"))

View File

View File

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

View File

@ -0,0 +1,9 @@
addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2")
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.2.0")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")
//addSbtPlugin("com.artima.supersafe" % "sbtplugin" % "1.1.2")

View File

@ -0,0 +1,20 @@
package org.bigbluebutton.client;
public class ConnInfo {
public final String meetingId;
public final String userId;
public final String token;
public final String connId;
public final String sessionId;
public ConnInfo(String meetingId, String userId, String token,
String connId, String sessionId) {
this.meetingId = meetingId;
this.userId = userId;
this.token = token;
this.connId = connId;
this.sessionId = sessionId;
}
}

View File

@ -0,0 +1,10 @@
package org.bigbluebutton.client;
public interface IClientInGW {
void connect(ConnInfo connInfo);
void disconnect(ConnInfo connInfo);
void handleMsgFromClient(ConnInfo connInfo, String json);
void send(String channel, String json);
}

View File

@ -8,7 +8,7 @@ import org.bigbluebutton.common.messages.EditCaptionHistoryMessage;
import org.bigbluebutton.common.messages.SendCaptionHistoryReplyMessage;
import org.bigbluebutton.common.messages.UpdateCaptionOwnerMessage;
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
import com.google.gson.Gson;
@ -16,9 +16,9 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class CaptionClientMessageSender {
private ConnectionInvokerService service;
private IConnectionInvokerService service;
public CaptionClientMessageSender(ConnectionInvokerService service) {
public CaptionClientMessageSender(IConnectionInvokerService service) {
this.service = service;
}

View File

@ -1,25 +1,24 @@
package org.bigbluebutton.red5.client;
import java.util.HashMap;
import java.util.Map;
import org.bigbluebutton.common.messages.GetChatHistoryReplyMessage;
import org.bigbluebutton.common.messages.SendPrivateChatMessage;
import org.bigbluebutton.common.messages.SendPublicChatMessage;
import org.bigbluebutton.common.messages.ClearPublicChatHistoryReplyMessage;
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
import org.bigbluebutton.red5.service.ChatKeyUtil;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.bigbluebutton.common.messages.ClearPublicChatHistoryReplyMessage;
import org.bigbluebutton.common.messages.GetChatHistoryReplyMessage;
import org.bigbluebutton.common.messages.SendPrivateChatMessage;
import org.bigbluebutton.common.messages.SendPublicChatMessage;
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
import org.bigbluebutton.red5.client.ChatKeyUtil;
import java.util.HashMap;
import java.util.Map;
public class ChatClientMessageSender {
private ConnectionInvokerService service;
private IConnectionInvokerService service;
public ChatClientMessageSender(ConnectionInvokerService service) {
public ChatClientMessageSender(IConnectionInvokerService service) {
this.service = service;
}

View File

@ -0,0 +1,13 @@
package org.bigbluebutton.red5.client;
public class ChatKeyUtil {
public static final String CHAT_TYPE = "chatType";
public static final String FROM_USERID = "fromUserID";
public static final String FROM_USERNAME = "fromUsername";
public static final String FROM_COLOR = "fromColor";
public static final String FROM_TIME = "fromTime";
public static final String FROM_TZ_OFFSET = "fromTimezoneOffset";
public static final String TO_USERID = "toUserID";
public static final String TO_USERNAME = "toUsername";
public static final String MESSAGE = "message";
}

View File

@ -6,16 +6,16 @@ import java.util.Map;
import org.bigbluebutton.common.messages.DeskShareNotifyASingleViewerEventMessage;
import org.bigbluebutton.common.messages.DeskShareNotifyViewersRTMPEventMessage;
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class DeskShareMessageSender {
private ConnectionInvokerService service;
private IConnectionInvokerService service;
public DeskShareMessageSender(ConnectionInvokerService service) {
public DeskShareMessageSender(IConnectionInvokerService service) {
this.service = service;
}

View File

@ -0,0 +1,5 @@
package org.bigbluebutton.red5.client;
public interface IReceivedOldMessageHandler {
void handleMessage(String pattern, String channel, String message);
}

View File

@ -16,7 +16,7 @@ import org.bigbluebutton.common.messages.MeetingStateMessage;
import org.bigbluebutton.common.messages.NewPermissionsSettingMessage;
import org.bigbluebutton.common.messages.UserLockedMessage;
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
import org.bigbluebutton.red5.client.messaging.DisconnectAllClientsMessage;
import org.bigbluebutton.red5.client.messaging.DisconnectClientMessage;
@ -27,9 +27,9 @@ import com.google.gson.JsonParser;
public class MeetingClientMessageSender {
private ConnectionInvokerService service;
private IConnectionInvokerService service;
public MeetingClientMessageSender(ConnectionInvokerService service) {
public MeetingClientMessageSender(IConnectionInvokerService service) {
this.service = service;
}

View File

@ -5,16 +5,16 @@ import java.util.HashMap;
import java.util.Map;
import org.bigbluebutton.common.messages.*;
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class PollingClientMessageSender {
private ConnectionInvokerService service;
private IConnectionInvokerService service;
public PollingClientMessageSender(ConnectionInvokerService service) {
public PollingClientMessageSender(IConnectionInvokerService service) {
this.service = service;
}

View File

@ -15,7 +15,7 @@ import org.bigbluebutton.common.messages.PresentationPageResizedMessage;
import org.bigbluebutton.common.messages.PresentationRemovedMessage;
import org.bigbluebutton.common.messages.PresentationSharedMessage;
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
import com.google.gson.Gson;
@ -23,9 +23,9 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class PresentationClientMessageSender {
private ConnectionInvokerService service;
private IConnectionInvokerService service;
public PresentationClientMessageSender(ConnectionInvokerService service) {
public PresentationClientMessageSender(IConnectionInvokerService service) {
this.service = service;
}

View File

@ -9,7 +9,7 @@ import org.bigbluebutton.common.messages.CreateAdditionalNotesReplyMessage;
import org.bigbluebutton.common.messages.DestroyAdditionalNotesReplyMessage;
import org.bigbluebutton.common.messages.SharedNotesSyncNoteReplyMessage;
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
import com.google.gson.Gson;
@ -17,9 +17,9 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class SharedNotesClientMessageSender {
private ConnectionInvokerService service;
private IConnectionInvokerService service;
public SharedNotesClientMessageSender(ConnectionInvokerService service) {
public SharedNotesClientMessageSender(IConnectionInvokerService service) {
this.service = service;
}

View File

@ -39,9 +39,8 @@ import org.bigbluebutton.messages.BreakoutRoomsTimeRemainingUpdate;
import org.bigbluebutton.messages.TimeRemainingUpdate;
import org.bigbluebutton.messages.UpdateBreakoutUsers;
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;
import com.google.gson.Gson;
@ -49,11 +48,10 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class UserClientMessageSender {
private static Logger log = Red5LoggerFactory.getLogger(UserClientMessageSender.class, "bigbluebutton");
private ConnectionInvokerService service;
private IConnectionInvokerService service;
public UserClientMessageSender(ConnectionInvokerService service) {
public UserClientMessageSender(IConnectionInvokerService service) {
this.service = service;
}
@ -319,8 +317,7 @@ public class UserClientMessageSender {
Map<String, Object> message = new HashMap<String, Object>();
Gson gson = new Gson();
message.put("msg", gson.toJson(args));
log.info("validateAuthTokenReply - " + gson.toJson(args));
DirectClientMessage m = new DirectClientMessage(msg.meetingId, msg.userId, "validateAuthTokenReply", message);
service.sendMessage(m);
}
@ -333,8 +330,7 @@ public class UserClientMessageSender {
Map<String, Object> message = new HashMap<String, Object>();
Gson gson = new Gson();
message.put("msg", gson.toJson(args));
log.info("validateAuthTokenTimedOut - " + gson.toJson(args));
DirectClientMessage m = new DirectClientMessage(msg.meetingId, msg.userId, "validateAuthTokenTimedOut", message);
service.sendMessage(m);
}
@ -360,7 +356,6 @@ public class UserClientMessageSender {
message.put("msg", gson.toJson(args));
String userId = msg.user.get("userId").toString();
log.info("joinMeetingReply - " + gson.toJson(args));
DirectClientMessage jmr = new DirectClientMessage(msg.meetingId, userId, "joinMeetingReply", message);
service.sendMessage(jmr);

View File

@ -11,7 +11,7 @@ import org.bigbluebutton.common.messages.SendWhiteboardAnnotationReplyMessage;
import org.bigbluebutton.common.messages.UndoWhiteboardReplyMessage;
import org.bigbluebutton.common.messages.ModifiedWhiteboardAccessMessage;
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.IConnectionInvokerService;
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
import com.google.gson.Gson;
@ -19,9 +19,9 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class WhiteboardClientMessageSender {
private ConnectionInvokerService service;
private IConnectionInvokerService service;
public WhiteboardClientMessageSender(ConnectionInvokerService service) {
public WhiteboardClientMessageSender(IConnectionInvokerService service) {
this.service = service;
}

View File

@ -0,0 +1,19 @@
package org.bigbluebutton.red5.client.messaging;
public class BroadcastToMeetingMsg implements ClientMessage {
public final String meetingId;
public final String messageName;
public final String json;
public BroadcastToMeetingMsg(String meetingId, String messageName, String json) {
this.meetingId = meetingId;
this.messageName = messageName;
this.json = json;
}
public String getMessageName() {
return messageName;
}
}

View File

@ -0,0 +1,21 @@
package org.bigbluebutton.red5.client.messaging;
public class DirectToClientMsg implements ClientMessage{
public final String meetingId;
public final String connId;
public final String json;
public final String messageName;
public DirectToClientMsg(String meetingId, String connId, String messageName, String json) {
this.meetingId = meetingId;
this.connId = connId;
this.messageName = messageName;
this.json = json;
}
public String getMessageName() {
return messageName;
}
}

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