Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: Initial migration of bbb-web APIs #19918

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ cache/*
artifacts/*
bbb-presentation-video.zip
bbb-presentation-video
bbb-core-api/gen
bbb-graphql-actions-adapter-server/
bigbluebutton-html5/public/locales/index.json
3 changes: 3 additions & 0 deletions akka-bbb-apps/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.typesafe.sbt.SbtNativePackager.autoImport._
enablePlugins(JavaServerAppPackaging)
enablePlugins(UniversalPlugin)
enablePlugins(DebianPlugin)
enablePlugins(PekkoGrpcPlugin)

version := "0.0.4"

Expand Down Expand Up @@ -44,6 +45,8 @@ testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/sc
Seq(Revolver.settings: _*)
lazy val bbbAppsAkka = (project in file(".")).settings(name := "bbb-apps-akka", libraryDependencies ++= Dependencies.runtime).settings(compileSettings)

PB.protoSources in Compile += baseDirectory.value / "../bbb-common-grpc/src/main/proto"

// See https://github.com/scala-ide/scalariform
// Config file is in ./.scalariform.conf
scalariformAutoformat := true
Expand Down
4 changes: 4 additions & 0 deletions akka-bbb-apps/project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.15")
addSbtPlugin("net.vonbuchholtz" % "sbt-dependency-check" % "0.2.9")

addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")

addSbtPlugin("org.apache.pekko" % "pekko-grpc-sbt-plugin" % "1.0.1")

ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always
23 changes: 21 additions & 2 deletions akka-bbb-apps/src/main/scala/org/bigbluebutton/Boot.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package org.bigbluebutton

import org.apache.pekko.actor.ActorSystem
import org.apache.pekko.actor.{ActorRef, ActorSystem}
import org.apache.pekko.event.Logging
import org.apache.pekko.http.scaladsl.Http
import org.apache.pekko.http.scaladsl.model.{HttpRequest, HttpResponse}
import org.apache.pekko.stream.ActorMaterializer
import org.bigbluebutton.common2.redis.{MessageSender, RedisConfig, RedisPublisher}
import org.bigbluebutton.core._
Expand All @@ -12,7 +13,10 @@ import org.bigbluebutton.core2.AnalyticsActor
import org.bigbluebutton.core2.FromAkkaAppsMsgSenderActor
import org.bigbluebutton.endpoint.redis.{AppsRedisSubscriberActor, ExportAnnotationsActor, GraphqlConnectionsActor, LearningDashboardActor, RedisRecorderActor}
import org.bigbluebutton.common2.bus.IncomingJsonMessageBus
import org.bigbluebutton.service.{HealthzService, MeetingInfoActor, MeetingInfoService}
import org.bigbluebutton.service.{BbbCoreServiceImpl, HealthzService, MeetingInfoActor, MeetingInfoService}
import org.bigbluebutton.protos._

import scala.concurrent.{ExecutionContext, Future}

object Boot extends App with SystemConfiguration {

Expand Down Expand Up @@ -115,4 +119,19 @@ object Boot extends App with SystemConfiguration {
)

val bindingFuture = Http().bindAndHandle(apiService.routes, httpHost, httpPort)

new GrpcServer(system, bbbActor, grpcHost, grpcPort).run()
}

class GrpcServer(system: ActorSystem, bbbActor: ActorRef, host: String, port: Int) {
def run(): Future[Http.ServerBinding] = {
implicit val sys: ActorSystem = system
implicit val ec: ExecutionContext = sys.dispatcher
implicit val bbb: ActorRef = bbbActor

val service: HttpRequest => Future[HttpResponse] = BbbCoreServiceHandler(new BbbCoreServiceImpl())
val binding = Http().newServerAt(host, port).bind(service)
binding.foreach { binding => println(s"gRPC server bound to ${binding.localAddress}")}
binding
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,7 @@ trait SystemConfiguration {
val httpHost = config.getString("http.interface")
// Grab the "port" parameter from the http config
val httpPort = config.getInt("http.port")

val grpcHost = config.getString("grpc.interface")
val grpcPort = config.getInt("grpc.port")
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ class BigBlueButtonActor(
def receive = {
// Internal messages
case msg: DestroyMeetingInternalMsg => handleDestroyMeeting(msg)
case msg: IsMeetingRunning => handleIsMeetingRunning(sender(), msg)
case msg: GetMeeting => handleGetMeeting(sender(), msg)
case msg: GetMeetings => handleGetMeetings(sender(), msg)

// 2x messages
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
Expand Down Expand Up @@ -205,4 +208,29 @@ class BigBlueButtonActor(
}
}

private def handleIsMeetingRunning(sender: ActorRef, msg: IsMeetingRunning): Unit = {
RunningMeetings.findWithId(meetings, msg.meetingId) match {
case Some(_) => sender ! true
case None =>
RunningMeetings.findWithExtId(meetings, msg.meetingId) match {
case Some(_) => sender ! true
case None => sender ! false
}
}
}

private def handleGetMeeting(sender: ActorRef, msg: GetMeeting): Unit = {
RunningMeetings.findWithId(meetings, msg.meetingId) match {
case Some(m) => sender ! Some(m)
case None =>
RunningMeetings.findWithExtId(meetings, msg.meetingId) match {
case Some(m) => sender ! Some(m)
case None => sender ! None
}
}
}

private def handleGetMeetings(sender: ActorRef, msg: GetMeetings): Unit = {
sender ! RunningMeetings.meetingsMap(meetings)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,16 @@ case class UserClosedAllGraphqlConnectionsInternalMsg(userId: String) extends In
* @param userId
*/
case class UserEstablishedGraphqlConnectionInternalMsg(userId: String) extends InMessage

// DeskShare
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
case class DeskShareStoppedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
case class DeskShareRTMPBroadcastStartedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
case class DeskShareRTMPBroadcastStoppedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
case class DeskShareGetDeskShareInfoRequest(conferenceName: String, requesterID: String, replyTo: String) extends InMessage

// gRPC messages
case class IsMeetingRunning(meetingId: String) extends InMessage
case class GetMeeting(meetingId: String) extends InMessage
case class GetMeetings() extends InMessage
case class GetMeetingInfo() extends InMessage
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers {
}

private def handleSuccessfulUserJoin(msg: UserJoinMeetingReqMsg, regUser: RegisteredUser) = {
if (Users2x.numUsers(liveMeeting.users2x) == 0) meetingEndTime = 0
val newState = userJoinMeeting(outGW, msg.body.authToken, msg.body.clientType, liveMeeting, state)
updateParentMeetingWithNewListOfUsers()
notifyPreviousUsersWithSameExtId(regUser)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,7 @@ trait UserLeftVoiceConfEvtMsgHdlr {
if (liveMeeting.props.meetingProp.isBreakout) {
BreakoutHdlrHelpers.updateParentMeetingWithUsers(liveMeeting, eventBus)
}

if (Users2x.numUsers(liveMeeting.users2x) == 0) meetingEndTime = System.currentTimeMillis()
paultrudel marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ class MeetingActor(
with SyncGetMeetingInfoRespMsgHdlr
with ClientToServerLatencyTracerMsgHdlr
with ValidateConnAuthTokenSysMsgHdlr
with UserActivitySignCmdMsgHdlr {
with UserActivitySignCmdMsgHdlr

with GetMeetingInfoMsgHdlr {

object CheckVoiceRecordingInternalMsg
object SyncVoiceUserStatusInternalMsg
Expand Down Expand Up @@ -176,6 +178,8 @@ class MeetingActor(

// Send new 2x message
val msgEvent = MsgBuilder.buildMeetingCreatedEvtMsg(liveMeeting.props.meetingProp.intId, liveMeeting.props)
val meetingStartTme = System.currentTimeMillis()
var meetingEndTime = 0L
outGW.send(msgEvent)

//Insert meeting into the database
Expand Down Expand Up @@ -255,17 +259,19 @@ class MeetingActor(
//=============================

// 2x messages
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)
case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg)

// Handling RegisterUserReqMsg as it is forwarded from BBBActor and
// its type is not BbbCommonEnvCoreMsg
case m: RegisterUserReqMsg => usersApp.handleRegisterUserReqMsg(m)
case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m)
case m: GetRunningMeetingStateReqMsg => handleGetRunningMeetingStateReqMsg(m)
case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)
case m: RegisterUserReqMsg => usersApp.handleRegisterUserReqMsg(m)
case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m)
case m: GetRunningMeetingStateReqMsg => handleGetRunningMeetingStateReqMsg(m)
case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)

// Meeting
case m: DestroyMeetingSysCmdMsg => handleDestroyMeetingSysCmdMsg(m)
case m: DestroyMeetingSysCmdMsg =>
meetingEndTime = System.currentTimeMillis()
handleDestroyMeetingSysCmdMsg(m)

//======================================

Expand All @@ -280,6 +286,9 @@ class MeetingActor(
state = handleUserEstablishedGraphqlConnectionInternalMsg(msg, state)
updateModeratorsPresence()

// Internal gRPC messages
case msg: GetMeetingInfo => sender() ! handleGetMeetingInfo()

case msg: ExtendMeetingDuration => handleExtendMeetingDuration(msg)
case msg: SendTimeRemainingAuditInternalMsg =>
if (!liveMeeting.props.meetingProp.isBreakout) {
Expand Down Expand Up @@ -398,7 +407,9 @@ class MeetingActor(
private def handleMessageThatAffectsInactivity(msg: BbbCommonEnvCoreMsg): Unit = {

msg.core match {
case m: EndMeetingSysCmdMsg => handleEndMeeting(m, state)
case m: EndMeetingSysCmdMsg =>
meetingEndTime = System.currentTimeMillis()
paultrudel marked this conversation as resolved.
Show resolved Hide resolved
handleEndMeeting(m, state)

// Users
case m: ValidateAuthTokenReqMsg => state = usersApp.handleValidateAuthTokenReqMsg(m, state)
Expand Down Expand Up @@ -973,6 +984,7 @@ class MeetingActor(
Users2x.numUsers(liveMeeting.users2x) == 0
&& !state.expiryTracker.lastUserLeftOnInMs.isDefined) {
log.info("Setting meeting no more users. meetingId=" + props.meetingProp.intId)
meetingEndTime = System.currentTimeMillis()
val tracker = state.expiryTracker.setLastUserLeftOn(TimeUtil.timeNowInMs())
state.update(tracker)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ package org.bigbluebutton.core2
import org.bigbluebutton.core.db.MeetingDAO
import org.bigbluebutton.core.running.RunningMeeting

import scala.collection.immutable.VectorMap

object RunningMeetings {
def findWithId(meetings: RunningMeetings, id: String): Option[RunningMeeting] = {
meetings.toVector.find(m => m.props.meetingProp.intId == id)
}

def findWithExtId(meetings: RunningMeetings, id: String): Option[RunningMeeting] = {
meetings.toVector.find(m => m.props.meetingProp.extId == id)
}

def add(meetings: RunningMeetings, meeting: RunningMeeting): RunningMeeting = {
meetings.save(meeting)
meeting
Expand All @@ -25,17 +31,23 @@ object RunningMeetings {
meetings.toVector
}

def meetingsMap(meetings: RunningMeetings): VectorMap[String, RunningMeeting] = {
meetings.getMeetings
}

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 var meetings: VectorMap[String, RunningMeeting] = VectorMap.empty

private def toVector: Vector[RunningMeeting] = meetings.values.toVector

private def getMeetings: VectorMap[String, RunningMeeting] = meetings

private def save(meeting: RunningMeeting): RunningMeeting = {
meetings += meeting.props.meetingProp.intId -> meeting
meeting
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.bigbluebutton.core2.message.handlers

import org.bigbluebutton.core.models.{ RegisteredUsers, Users2x, VoiceUsers }
import org.bigbluebutton.core.models.VoiceUsers.findAllListenOnlyVoiceUsers
import org.bigbluebutton.core.models.Webcams.findAll
import org.bigbluebutton.core.running.MeetingActor
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core2.MeetingStatus2x.hasAuthedUserJoined
import org.bigbluebutton.protos.{ BreakoutInfo, DurationInfo, MeetingInfo, ParticipantInfo, User }

trait GetMeetingInfoMsgHdlr {
this: MeetingActor =>

def handleGetMeetingInfo(): MeetingInfo = {
val users = for {
u <- Users2x.findAll(liveMeeting.users2x)
} yield {
User(
userId = u.intId,
fullName = u.name,
role = u.role,
isPresenter = u.presenter,
isListeningOnly = findAllListenOnlyVoiceUsers(liveMeeting.voiceUsers).exists(v => v.intId == u.intId),
hasJoinedVoice = VoiceUsers.findAllNonListenOnlyVoiceUsers(liveMeeting.voiceUsers).exists(v => v.intId == u.intId),
hasVideo = findAll(liveMeeting.webcams).exists(w => w.userId == u.intId),
paultrudel marked this conversation as resolved.
Show resolved Hide resolved
clientType = u.clientType,
customData = RegisteredUsers.findWithUserId(u.intId, liveMeeting.registeredUsers).get.customParameters
)
}

val durationInfo = DurationInfo(
createTime = liveMeeting.props.durationProps.createdTime,
createdOn = liveMeeting.props.durationProps.createdDate,
duration = liveMeeting.props.durationProps.duration,
startTime = meetingStartTme,
endTime = meetingEndTime,
isRunning = MeetingStatus2x.hasMeetingEnded(liveMeeting.status),
hasBeenForciblyEnded = false
)

val lc = findAllListenOnlyVoiceUsers(liveMeeting.voiceUsers).length
val participantInfo = ParticipantInfo(
hasUserJoined = hasAuthedUserJoined(liveMeeting.status),
participantCount = Users2x.findAll(liveMeeting.users2x).length,
listenerCount = lc,
voiceParticipantCount = VoiceUsers.findAll(liveMeeting.voiceUsers).length - lc,
videoCount = findAll(liveMeeting.webcams).length,
maxUsers = liveMeeting.props.usersProp.maxUsers,
moderatorCount = Users2x.findAll(liveMeeting.users2x).count(u => u.role.equalsIgnoreCase("moderator"))
)

val breakoutInfo = BreakoutInfo(
isBreakout = liveMeeting.props.meetingProp.isBreakout,
parentMeetingId = liveMeeting.props.breakoutProps.parentId,
sequence = liveMeeting.props.breakoutProps.sequence,
freeJoin = liveMeeting.props.breakoutProps.freeJoin
)

MeetingInfo(
meetingName = liveMeeting.props.meetingProp.name,
meetingExtId = liveMeeting.props.meetingProp.extId,
meetingIntId = liveMeeting.props.meetingProp.intId,
voiceBridge = liveMeeting.props.voiceProp.voiceConf,
dialNumber = liveMeeting.props.voiceProp.dialNumber,
attendeePw = liveMeeting.props.password.viewerPass,
moderatorPw = liveMeeting.props.password.moderatorPass,
recording = liveMeeting.props.recordProp.record,
users = users,
metadata = liveMeeting.props.metadataProp.metadata,
breakoutRooms = if (state.breakout.isDefined) state.breakout.get.getRooms().map(_.name).toList else List(),
durationInfo = Some(durationInfo),
participantInfo = Some(participantInfo),
breakoutInfo = Some(breakoutInfo)
)
}
}