From 1f50ec2da70d16d03939eab51f9bc7f9e08daa9f Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Mon, 17 Jul 2017 09:13:07 -0700 Subject: [PATCH 01/10] - send destroy meeting to BBBActor when meeting ends --- .../core/BigBlueButtonActor.scala | 82 ++++++------------- .../core/BigBlueButtonInGW.scala | 2 +- .../bigbluebutton/core/api/InMessages.scala | 11 ++- ...ntationConversionCompletedPubMsgHdlr.scala | 2 - ...esentationConversionUpdatePubMsgHdlr.scala | 2 +- .../PresentationPageGeneratedPubMsgHdlr.scala | 1 - .../users/LogoutAndEndMeetingCmdMsgHdlr.scala | 50 ++++------- .../MeetingActivityResponseCmdMsgHdlr.scala | 5 +- .../core/apps/users/UsersApp.scala | 4 +- .../core/running/HandlerHelpers.scala | 50 ++++++++++- .../core/running/MeetingActor.scala | 27 +++--- .../core/running/MeetingActorAudit.scala | 2 +- ...scala => MeetingExpiryTrackerHelper.scala} | 30 +++++-- .../MeetingInactivityTrackerHelper.scala | 24 +++++- .../meeting/DestroyMeetingSysCmdMsgHdlr.scala | 1 - .../meeting/EndMeetingSysCmdMsgHdlr.scala | 22 +++-- .../handlers/users/UserJoiningHdlr.scala | 49 ----------- 17 files changed, 178 insertions(+), 186 deletions(-) rename akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/{MeetingExpiryTracker.scala => MeetingExpiryTrackerHelper.scala} (58%) mode change 100644 => 100755 akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/DestroyMeetingSysCmdMsgHdlr.scala delete mode 100755 akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserJoiningHdlr.scala diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala index 8448dee068..3b5c7e9fa2 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala @@ -59,21 +59,21 @@ class BigBlueButtonActor( } def receive = { - // 2x messages - case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg) + // Internal messages + case msg: DestroyMeetingInternalMsg => handleDestroyMeeting(msg) - case msg: ValidateAuthToken => handleValidateAuthToken(msg) - case _ => // do nothing + // 2x messages + case msg: BbbCommonEnvCoreMsg => handleBbbCommonEnvCoreMsg(msg) + case _ => // do nothing } private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = { msg.core match { - case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m) - case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m) - case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m) - case m: PubSubPingSysReqMsg => handlePubSubPingSysReqMsg(m) - case m: DestroyMeetingSysCmdMsg => handleDestroyMeeting(m) - case _ => log.warning("Cannot handle " + msg.envelope.name) + case m: CreateMeetingReqMsg => handleCreateMeetingReqMsg(m) + case m: RegisterUserReqMsg => handleRegisterUserReqMsg(m) + case m: GetAllMeetingsReqMsg => handleGetAllMeetingsReqMsg(m) + case m: PubSubPingSysReqMsg => handlePubSubPingSysReqMsg(m) + case _ => log.warning("Cannot handle " + msg.envelope.name) } } @@ -125,68 +125,38 @@ class BigBlueButtonActor( }) } - private def handleValidateAuthToken(msg: ValidateAuthToken) { - 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 handlePubSubPingSysReqMsg(msg: PubSubPingSysReqMsg): Unit = { val event = MsgBuilder.buildPubSubPongSysRespMsg(msg.body.system, msg.body.timestamp) outGW.send(event) } - private def handleDestroyMeeting(msg: DestroyMeetingSysCmdMsg): Unit = { + private def handleDestroyMeeting(msg: DestroyMeetingInternalMsg): Unit = { for { - m <- RunningMeetings.findWithId(meetings, msg.body.meetingId) - m2 <- RunningMeetings.remove(meetings, msg.body.meetingId) + m <- RunningMeetings.findWithId(meetings, msg.meetingId) + m2 <- RunningMeetings.remove(meetings, msg.meetingId) } yield { - // send the message for MeetingActor to handle too - m.actorRef ! msg + /** Unsubscribe to meeting and voice events. **/ + eventBus.unsubscribe(m.actorRef, m.props.meetingProp.intId) + eventBus.unsubscribe(m.actorRef, m.props.voiceProp.voiceConf) + eventBus.unsubscribe(m.actorRef, m.props.screenshareProps.screenshareConf) - // Delay sending DisconnectAllUsers because of RTMPT connection being dropped before UserEject message arrives to the client + bbbMsgBus.unsubscribe(m.actorRef, m.props.meetingProp.intId) + bbbMsgBus.unsubscribe(m.actorRef, m.props.voiceProp.voiceConf) + bbbMsgBus.unsubscribe(m.actorRef, m.props.screenshareProps.screenshareConf) + + // Delay sending DisconnectAllUsers to allow messages to reach the client + // before the connections are closed. context.system.scheduler.scheduleOnce(Duration.create(2500, TimeUnit.MILLISECONDS)) { // Disconnect all clients - val disconnectEvnt = MsgBuilder.buildDisconnectAllClientsSysMsg(msg.body.meetingId) + val disconnectEvnt = MsgBuilder.buildDisconnectAllClientsSysMsg(msg.meetingId) outGW.send(disconnectEvnt) - log.info("Destroyed meetingId={}", msg.body.meetingId) - val destroyedEvent = MsgBuilder.buildMeetingDestroyedEvtMsg(msg.body.meetingId) + log.info("Destroyed meetingId={}", msg.meetingId) + val destroyedEvent = MsgBuilder.buildMeetingDestroyedEvtMsg(msg.meetingId) outGW.send(destroyedEvent) - /** Unsubscribe to meeting and voice events. **/ - eventBus.unsubscribe(m.actorRef, m.props.meetingProp.intId) - eventBus.unsubscribe(m.actorRef, m.props.voiceProp.voiceConf) - eventBus.unsubscribe(m.actorRef, m.props.screenshareProps.screenshareConf) - - bbbMsgBus.unsubscribe(m.actorRef, m.props.meetingProp.intId) - bbbMsgBus.unsubscribe(m.actorRef, m.props.voiceProp.voiceConf) - bbbMsgBus.unsubscribe(m.actorRef, m.props.screenshareProps.screenshareConf) - // Stop the meeting actor. context.stop(m.actorRef) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala index 2f46e0cb86..e5f80622dc 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala @@ -78,7 +78,7 @@ class BigBlueButtonInGW( eventBus.publish( BigBlueButtonEvent( "meeting-manager", - new DestroyMeeting( + new DestroyMeetingInternalMsg( meetingID ) ) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala index 4210b28772..6a95777e0c 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/api/InMessages.scala @@ -18,13 +18,18 @@ case class IsMeetingActorAliveMessage(meetingId: String) extends InMessage case class KeepAliveMessage(aliveID: String) extends InMessage ////////////////////////////////////////////////////////////////////////////// -// Meeting +// Internal Messages ///////////////////////////////////////////////////////////////////////////// -case class MonitorNumberOfUsers(meetingID: String) extends InMessage +case class MonitorNumberOfUsersInternalMsg(meetingID: String) extends InMessage case class SendTimeRemainingUpdate(meetingId: String) extends InMessage case class ExtendMeetingDuration(meetingId: String, userId: String) extends InMessage -case class DestroyMeeting(meetingID: String) extends InMessage +case class DestroyMeetingInternalMsg(meetingId: String) extends InMessage +case class BreakoutRoomEndedInternalMsg(meetingId: String) extends InMessage + +////////////////////////////////////////////////////////////////////////////// +// Meeting +///////////////////////////////////////////////////////////////////////////// case class StartMeeting(meetingID: String) extends InMessage case class EndMeeting(meetingId: String) extends InMessage case class LockSetting(meetingID: String, locked: Boolean, settings: Map[String, Boolean]) extends InMessage diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentation/PresentationConversionCompletedPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentation/PresentationConversionCompletedPubMsgHdlr.scala index 5d4448699d..cf41261718 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentation/PresentationConversionCompletedPubMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentation/PresentationConversionCompletedPubMsgHdlr.scala @@ -11,8 +11,6 @@ trait PresentationConversionCompletedPubMsgHdlr { val outGW: OutMessageGateway def handlePresentationConversionCompletedPubMsg(msg: PresentationConversionCompletedSysPubMsg): Unit = { - log.debug("**************** !!!!!PresentationConversionCompletedPubMsg ") - def broadcastEvent(msg: PresentationConversionCompletedSysPubMsg): Unit = { val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId) val envelope = BbbCoreEnvelope(PresentationConversionCompletedEvtMsg.NAME, routing) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentation/PresentationConversionUpdatePubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentation/PresentationConversionUpdatePubMsgHdlr.scala index 4f19b754ab..c9809cd0e3 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentation/PresentationConversionUpdatePubMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentation/PresentationConversionUpdatePubMsgHdlr.scala @@ -9,7 +9,7 @@ trait PresentationConversionUpdatePubMsgHdlr { val outGW: OutMessageGateway def handlePresentationConversionUpdatePubMsg(msg: PresentationConversionUpdateSysPubMsg): Unit = { - log.debug("**************** !!!!!PresentationConversionUpdateSysPubMsg " + msg.body.messageKey) + def broadcastEvent(msg: PresentationConversionUpdateSysPubMsg): Unit = { val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId) val envelope = BbbCoreEnvelope(PresentationConversionUpdateEvtMsg.NAME, routing) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentation/PresentationPageGeneratedPubMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentation/PresentationPageGeneratedPubMsgHdlr.scala index 710fa66dbf..467a50aef0 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentation/PresentationPageGeneratedPubMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentation/PresentationPageGeneratedPubMsgHdlr.scala @@ -9,7 +9,6 @@ trait PresentationPageGeneratedPubMsgHdlr { val outGW: OutMessageGateway def handlePresentationPageGeneratedPubMsg(msg: PresentationPageGeneratedSysPubMsg): Unit = { - log.debug("**************** !!!!!PresentationPageGeneratedSysPubMsg " + msg.body.messageKey) def broadcastEvent(msg: PresentationPageGeneratedSysPubMsg): Unit = { val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/LogoutAndEndMeetingCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/LogoutAndEndMeetingCmdMsgHdlr.scala index 464fea9efd..90a21bead6 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/LogoutAndEndMeetingCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/LogoutAndEndMeetingCmdMsgHdlr.scala @@ -2,56 +2,34 @@ package org.bigbluebutton.core.apps.users import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway -import org.bigbluebutton.core.api.{ EndMeeting, LogoutEndMeeting } +import org.bigbluebutton.core.bus.{ IncomingEventBus } import org.bigbluebutton.core.models.{ Roles, Users2x } -import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting } -import org.bigbluebutton.core2.MeetingStatus2x +import org.bigbluebutton.core.running.LiveMeeting trait LogoutAndEndMeetingCmdMsgHdlr { this: UsersApp => val liveMeeting: LiveMeeting val outGW: OutMessageGateway + val eventBus: IncomingEventBus def handleLogoutAndEndMeetingCmdMsg(msg: LogoutAndEndMeetingCmdMsg) { for { u <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) } yield { if (u.role == Roles.MODERATOR_ROLE) { - endMeeting() + endMeeting(outGW, liveMeeting) + + if (liveMeeting.props.meetingProp.isBreakout) { + log.info( + "Informing parent meeting {} that a breakout room has been ended {}", + liveMeeting.props.breakoutProps.parentId, liveMeeting.props.meetingProp.intId + ) + notifyParentThatBreakoutEnded(eventBus, liveMeeting) + } + + destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId) } } - - def endMeeting(): Unit = { - def buildMeetingEndingEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") - val envelope = BbbCoreEnvelope(MeetingEndingEvtMsg.NAME, routing) - val body = MeetingEndingEvtMsgBody(meetingId) - val header = BbbClientMsgHeader(MeetingEndingEvtMsg.NAME, meetingId, "not-used") - val event = MeetingEndingEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - val endingEvent = buildMeetingEndingEvtMsg(liveMeeting.props.meetingProp.intId) - - // Broadcast users the meeting will end - outGW.send(endingEvent) - - MeetingStatus2x.meetingHasEnded(liveMeeting.status) - - def buildMeetingEndedEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { - val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") - val envelope = BbbCoreEnvelope(MeetingEndedEvtMsg.NAME, routing) - val body = MeetingEndedEvtMsgBody(meetingId) - val header = BbbCoreBaseHeader(MeetingEndedEvtMsg.NAME) - val event = MeetingEndedEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - val endedEvnt = buildMeetingEndedEvtMsg(liveMeeting.props.meetingProp.intId) - outGW.send(endedEvnt) - } } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MeetingActivityResponseCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MeetingActivityResponseCmdMsgHdlr.scala index 38c6c98d4f..cdc8826a7c 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MeetingActivityResponseCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MeetingActivityResponseCmdMsgHdlr.scala @@ -13,9 +13,10 @@ trait MeetingActivityResponseCmdMsgHdlr { def handleMeetingActivityResponseCmdMsg( msg: MeetingActivityResponseCmdMsg, - tracker: MeetingInactivityTracker + tracker: MeetingInactivityTracker, + helper: MeetingInactivityTrackerHelper ): MeetingInactivityTracker = { - MeetingInactivityTrackerHelper.processMeetingActivityResponse( + helper.processMeetingActivityResponse( props = liveMeeting.props, outGW, msg, diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala index 236cfcfa26..ed9845298e 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala @@ -3,12 +3,14 @@ package org.bigbluebutton.core.apps.users import akka.actor.ActorContext import akka.event.Logging import org.bigbluebutton.core.OutMessageGateway +import org.bigbluebutton.core.bus.IncomingEventBus import org.bigbluebutton.core.running.LiveMeeting import org.bigbluebutton.core2.message.handlers.users.ValidateAuthTokenReqMsgHdlr class UsersApp( val liveMeeting: LiveMeeting, - val outGW: OutMessageGateway + val outGW: OutMessageGateway, + val eventBus: IncomingEventBus )(implicit val context: ActorContext) extends ValidateAuthTokenReqMsgHdlr diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala index 480214dc1d..b7fca04e41 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala @@ -1,13 +1,15 @@ package org.bigbluebutton.core.running +import org.bigbluebutton.SystemConfiguration import org.bigbluebutton.common2.msgs._ -import org.bigbluebutton.core.api.RecordingStatusChanged +import org.bigbluebutton.core.api.{ BreakoutRoomEndedInternalMsg, DestroyMeetingInternalMsg, RecordingStatusChanged } +import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus } import org.bigbluebutton.core.{ MessageRecorder, OutMessageGateway } import org.bigbluebutton.core.models._ import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender, UserJoinedMeetingEvtMsgBuilder } -trait HandlerHelpers { +trait HandlerHelpers extends SystemConfiguration { def validateTokenFailed(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String, valid: Boolean, waitForApproval: Boolean): Unit = { @@ -136,7 +138,6 @@ trait HandlerHelpers { val event = UserJoinedMeetingEvtMsgBuilder.build(liveMeeting.props.meetingProp.intId, userState) Sender.send(outGW, event) - MessageRecorder.record(outGW, liveMeeting.props.recordProp.record, event.core) startRecordingIfAutoStart2x(liveMeeting) } } @@ -165,4 +166,47 @@ trait HandlerHelpers { def event = MsgBuilder.buildPresenterAssignedEvtMsg(meetingId, intId, name, assignedBy) outGW.send(event) } + + def endMeeting(outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = { + def buildMeetingEndingEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") + val envelope = BbbCoreEnvelope(MeetingEndingEvtMsg.NAME, routing) + val body = MeetingEndingEvtMsgBody(meetingId) + val header = BbbClientMsgHeader(MeetingEndingEvtMsg.NAME, meetingId, "not-used") + val event = MeetingEndingEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + val endingEvent = buildMeetingEndingEvtMsg(liveMeeting.props.meetingProp.intId) + + // Broadcast users the meeting will end + outGW.send(endingEvent) + + MeetingStatus2x.meetingHasEnded(liveMeeting.status) + + def buildMeetingEndedEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { + val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") + val envelope = BbbCoreEnvelope(MeetingEndedEvtMsg.NAME, routing) + val body = MeetingEndedEvtMsgBody(meetingId) + val header = BbbCoreBaseHeader(MeetingEndedEvtMsg.NAME) + val event = MeetingEndedEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + val endedEvnt = buildMeetingEndedEvtMsg(liveMeeting.props.meetingProp.intId) + outGW.send(endedEvnt) + } + + def destroyMeeting(eventBus: IncomingEventBus, meetingId: String): Unit = { + eventBus.publish(BigBlueButtonEvent(meetingManagerChannel, new DestroyMeetingInternalMsg(meetingId))) + } + + def notifyParentThatBreakoutEnded(eventBus: IncomingEventBus, liveMeeting: LiveMeeting): Unit = { + eventBus.publish(BigBlueButtonEvent( + liveMeeting.props.breakoutProps.parentId, + new BreakoutRoomEndedInternalMsg(liveMeeting.props.meetingProp.intId) + )) + } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala index 955be5ab43..2b6efddad5 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala @@ -102,7 +102,7 @@ class MeetingActor( val captionApp2x = new CaptionApp2x(liveMeeting, outGW) val sharedNotesApp2x = new SharedNotesApp2x(liveMeeting, outGW) val chatApp2x = new ChatApp2x(liveMeeting, outGW) - val usersApp = new UsersApp(liveMeeting, outGW) + val usersApp = new UsersApp(liveMeeting, outGW, eventBus) var inactivityTracker = new MeetingInactivityTracker( liveMeeting.props.durationProps.maxInactivityTimeoutMinutes, @@ -110,11 +110,15 @@ class MeetingActor( TimeUtil.millisToMinutes(System.currentTimeMillis()), false, 0L ) + val inactivityTrackerHelper = new MeetingInactivityTrackerHelper(liveMeeting, outGW) + var expiryTracker = new MeetingExpiryTracker( TimeUtil.millisToMinutes(System.currentTimeMillis()), false, 0L ) + val expiryTrackerHelper = new MeetingExpiryTrackerHelper(liveMeeting, outGW) + /*******************************************************************/ //object FakeTestData extends FakeTestData //FakeTestData.createFakeUsers(liveMeeting) @@ -137,7 +141,7 @@ class MeetingActor( //======================================= // old messages - case msg: MonitorNumberOfUsers => handleMonitorNumberOfUsers(msg) + case msg: MonitorNumberOfUsersInternalMsg => handleMonitorNumberOfUsers(msg) case msg: AllowUserToShareDesktop => handleAllowUserToShareDesktop(msg) case msg: ExtendMeetingDuration => handleExtendMeetingDuration(msg) @@ -159,13 +163,14 @@ class MeetingActor( msg.core match { // Users - case m: ValidateAuthTokenReqMsg => usersApp.handleValidateAuthTokenReqMsg(m) - case m: UserJoinMeetingReqMsg => handleUserJoinMeetingReqMsg(m) - case m: UserLeaveReqMsg => handleUserLeaveReqMsg(m) - case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m) - case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m) + case m: ValidateAuthTokenReqMsg => usersApp.handleValidateAuthTokenReqMsg(m) + case m: UserJoinMeetingReqMsg => handleUserJoinMeetingReqMsg(m) + case m: UserLeaveReqMsg => handleUserLeaveReqMsg(m) + case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m) + case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m) case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m) - case m: MeetingActivityResponseCmdMsg => inactivityTracker = usersApp.handleMeetingActivityResponseCmdMsg(m, inactivityTracker) + case m: MeetingActivityResponseCmdMsg => + inactivityTracker = usersApp.handleMeetingActivityResponseCmdMsg(m, inactivityTracker, inactivityTrackerHelper) case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m) case m: SetRecordingStatusCmdMsg => usersApp.handleSetRecordingStatusCmdMsg(m) case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m) @@ -329,15 +334,15 @@ class MeetingActor( } } - def handleMonitorNumberOfUsers(msg: MonitorNumberOfUsers) { - inactivityTracker = MeetingInactivityTrackerHelper.processMeetingInactivityAudit( + def handleMonitorNumberOfUsers(msg: MonitorNumberOfUsersInternalMsg) { + inactivityTracker = inactivityTrackerHelper.processMeetingInactivityAudit( props = liveMeeting.props, outGW, eventBus, inactivityTracker ) - expiryTracker = MeetingExpiryTracker.processMeetingExpiryAudit(liveMeeting.props, expiryTracker, eventBus) + expiryTracker = expiryTrackerHelper.processMeetingExpiryAudit(liveMeeting.props, expiryTracker, eventBus) monitorNumberOfWebUsers() monitorNumberOfUsers() diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActorAudit.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActorAudit.scala index 26038632d7..729aabe2aa 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActorAudit.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActorAudit.scala @@ -72,7 +72,7 @@ class MeetingActorAudit( } def handleMonitorNumberOfWebUsers() { - eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, MonitorNumberOfUsers(props.meetingProp.intId))) + eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, MonitorNumberOfUsersInternalMsg(props.meetingProp.intId))) // Trigger updating users of time remaining on meeting. eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendTimeRemainingUpdate(props.meetingProp.intId))) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTracker.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala similarity index 58% rename from akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTracker.scala rename to akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala index 09ef4c6988..022e3318ab 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTracker.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala @@ -1,12 +1,19 @@ package org.bigbluebutton.core.running +import akka.actor.ActorContext +import akka.event.Logging import org.bigbluebutton.common2.domain.DefaultProps -import org.bigbluebutton.core.api.EndMeeting -import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus } +import org.bigbluebutton.core.OutMessageGateway +import org.bigbluebutton.core.bus.{ IncomingEventBus } import org.bigbluebutton.core.domain.MeetingExpiryTracker import org.bigbluebutton.core.util.TimeUtil -object MeetingExpiryTracker { +class MeetingExpiryTrackerHelper( + val liveMeeting: LiveMeeting, + val outGW: OutMessageGateway +)(implicit val context: ActorContext) extends HandlerHelpers { + + val log = Logging(context.system, getClass) def hasMeetingExpiredNeverBeenJoined(nowInMinutes: Long, startedOnInMinutes: Long, meetingExpireIfNoUserJoinedInMinutes: Long): Boolean = { nowInMinutes - startedOnInMinutes > meetingExpireIfNoUserJoinedInMinutes @@ -19,6 +26,7 @@ object MeetingExpiryTracker { def processNeverBeenJoinedExpiry(nowInMinutes: Long, props: DefaultProps, tracker: MeetingExpiryTracker, eventBus: IncomingEventBus): MeetingExpiryTracker = { if (hasMeetingExpiredNeverBeenJoined(nowInMinutes, tracker.startedOnInMinutes, props.durationProps.meetingExpireIfNoUserJoinedInMinutes)) { + log.info("Ending meeting as it has never been joined.") sendEndMeetingDueToExpiry(props, eventBus) tracker } else { @@ -32,7 +40,8 @@ object MeetingExpiryTracker { if (!tracker.meetingJoined) { processNeverBeenJoinedExpiry(nowInMinutes, props, tracker, eventBus) } else { - if (meetingOverDuration(nowInMinutes, tracker.startedOnInMinutes, props.durationProps.duration)) { + if (props.durationProps.duration != 0 && meetingOverDuration(nowInMinutes, tracker.startedOnInMinutes, props.durationProps.duration)) { + log.info("Ending meeting as it has passed duration.") sendEndMeetingDueToExpiry(props, eventBus) tracker } else { @@ -42,6 +51,17 @@ object MeetingExpiryTracker { } def sendEndMeetingDueToExpiry(props: DefaultProps, eventBus: IncomingEventBus): Unit = { - eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, EndMeeting(props.meetingProp.intId))) + + endMeeting(outGW, liveMeeting) + + if (liveMeeting.props.meetingProp.isBreakout) { + log.info( + "Informing parent meeting {} that a breakout room has been ended {}", + liveMeeting.props.breakoutProps.parentId, liveMeeting.props.meetingProp.intId + ) + notifyParentThatBreakoutEnded(eventBus, liveMeeting) + } + + destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId) } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingInactivityTrackerHelper.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingInactivityTrackerHelper.scala index a99f2ac352..0c929fb25d 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingInactivityTrackerHelper.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingInactivityTrackerHelper.scala @@ -1,15 +1,21 @@ package org.bigbluebutton.core.running +import akka.actor.ActorContext +import akka.event.Logging import org.bigbluebutton.core.domain.MeetingInactivityTracker import com.softwaremill.quicklens._ import org.bigbluebutton.common2.domain.DefaultProps import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway -import org.bigbluebutton.core.api.EndMeeting -import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus } +import org.bigbluebutton.core.bus.{ IncomingEventBus } import org.bigbluebutton.core.util.TimeUtil -object MeetingInactivityTrackerHelper { +class MeetingInactivityTrackerHelper( + val liveMeeting: LiveMeeting, + val outGW: OutMessageGateway +)(implicit val context: ActorContext) extends HandlerHelpers { + + val log = Logging(context.system, getClass) def isMeetingActive( nowInMinutes: Long, @@ -67,7 +73,17 @@ object MeetingInactivityTrackerHelper { } def sendEndMeetingDueToInactivity(props: DefaultProps, eventBus: IncomingEventBus): Unit = { - eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, EndMeeting(props.meetingProp.intId))) + endMeeting(outGW, liveMeeting) + + if (liveMeeting.props.meetingProp.isBreakout) { + log.info( + "Informing parent meeting {} that a breakout room has been ended {}", + liveMeeting.props.breakoutProps.parentId, liveMeeting.props.meetingProp.intId + ) + notifyParentThatBreakoutEnded(eventBus, liveMeeting) + } + + destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId) } def sendMeetingInactivityWarning(props: DefaultProps, outGW: OutMessageGateway, timeLeftSeconds: Long): Unit = { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/DestroyMeetingSysCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/DestroyMeetingSysCmdMsgHdlr.scala old mode 100644 new mode 100755 index bb60cf6a13..d3005f6ab8 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/DestroyMeetingSysCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/DestroyMeetingSysCmdMsgHdlr.scala @@ -3,7 +3,6 @@ package org.bigbluebutton.core2.message.handlers.meeting import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting } -import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.message.senders.MsgBuilder trait DestroyMeetingSysCmdMsgHdlr { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/EndMeetingSysCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/EndMeetingSysCmdMsgHdlr.scala index 91e543b789..597f0e7ed8 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/EndMeetingSysCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/EndMeetingSysCmdMsgHdlr.scala @@ -2,24 +2,28 @@ package org.bigbluebutton.core2.message.handlers.meeting import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway -import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting } -import org.bigbluebutton.core2.MeetingStatus2x -import org.bigbluebutton.core2.message.senders.MsgBuilder +import org.bigbluebutton.core.bus.IncomingEventBus +import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting } -trait EndMeetingSysCmdMsgHdlr { +trait EndMeetingSysCmdMsgHdlr extends HandlerHelpers { this: BaseMeetingActor => val liveMeeting: LiveMeeting val outGW: OutMessageGateway + val eventBus: IncomingEventBus def handleEndMeeting(msg: EndMeetingSysCmdMsg) { - // Broadcast users the meeting will end - outGW.send(MsgBuilder.buildMeetingEndingEvtMsg(liveMeeting.props.meetingProp.intId)) + endMeeting(outGW, liveMeeting) - MeetingStatus2x.meetingHasEnded(liveMeeting.status) + if (liveMeeting.props.meetingProp.isBreakout) { + log.info( + "Informing parent meeting {} that a breakout room has been ended {}", + liveMeeting.props.breakoutProps.parentId, liveMeeting.props.meetingProp.intId + ) + notifyParentThatBreakoutEnded(eventBus, liveMeeting) + } - // Sent from akka-apps to bbb-web to inform about end of meeting - outGW.send(MsgBuilder.buildMeetingEndedEvtMsg(liveMeeting.props.meetingProp.intId)) + destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId) } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserJoiningHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserJoiningHdlr.scala deleted file mode 100755 index cb7f9a5f1b..0000000000 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserJoiningHdlr.scala +++ /dev/null @@ -1,49 +0,0 @@ -package org.bigbluebutton.core2.message.handlers.users - -import org.bigbluebutton.common2.domain.VoiceUserVO -import org.bigbluebutton.core.OutMessageGateway -import org.bigbluebutton.core.api._ -import org.bigbluebutton.core.models._ -import org.bigbluebutton.core.running.MeetingActor -import org.bigbluebutton.core2.MeetingStatus2x - -trait UserJoiningHdlr { - this: MeetingActor => - - val outGW: OutMessageGateway - def handleUserJoin(msg: UserJoining): Unit = { - log.debug("Received user joined meeting. metingId=" + props.meetingProp.intId + " userId=" + msg.userID) - - /* - - for { - uvo <- Users.newUser(msg.userID, lockStatus, ru, waitingForAcceptance, vu, liveMeeting.users) - } yield { - log.info("User joined meeting. metingId=" + props.meetingProp.intId + " userId=" + uvo.id + " user=" + uvo) - - 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(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=" + props.meetingProp.intId + " userId=" + uvo.id) - assignNewPresenter(msg.userID, ru.name, msg.userID) - } - } - } else { - log.info("User waiting for acceptance. metingId=" + props.meetingProp.intId + " userId=" + uvo.id) - } - liveMeeting.webUserJoined - startRecordingIfAutoStart() - } - } - */ - - } -} From 61147dbb675b13c12f14617b209dbf74c4c728cf Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Mon, 17 Jul 2017 14:30:08 -0700 Subject: [PATCH 02/10] - try passing state around to track meeting expiry --- .../MeetingActivityResponseCmdMsgHdlr.scala | 18 +- .../core/apps/users/UserLeaveReqMsgHdlr.scala | 12 +- .../domain/MeetingInactivityTracker.scala | 92 +- .../core/domain/MeetingState2x.scala | 11 + .../core/running/HandlerHelpers.scala | 437 ++++----- .../core/running/LiveMeeting.scala | 23 - .../core/running/MeetingActor.scala | 867 +++++++++--------- .../running/MeetingExpiryTrackerHelper.scala | 36 +- .../MeetingInactivityTrackerHelper.scala | 228 ++--- .../core/running/RunningMeeting.scala | 2 +- .../bigbluebutton/core/util/TimeUtil.scala | 4 + .../bigbluebutton/core2/MeetingStatus2x.scala | 16 +- .../SendTimeRemainingUpdateHdlr.scala | 16 +- .../users/UserJoinMeetingReqMsgHdlr.scala | 9 +- .../users/ValidateAuthTokenReqMsgHdlr.scala | 12 +- 15 files changed, 911 insertions(+), 872 deletions(-) create mode 100755 akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingState2x.scala diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MeetingActivityResponseCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MeetingActivityResponseCmdMsgHdlr.scala index cdc8826a7c..a6f52251ce 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MeetingActivityResponseCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MeetingActivityResponseCmdMsgHdlr.scala @@ -2,7 +2,7 @@ package org.bigbluebutton.core.apps.users import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway -import org.bigbluebutton.core.domain.MeetingInactivityTracker +import org.bigbluebutton.core.domain.{ MeetingInactivityTracker, MeetingState2x } import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting, MeetingInactivityTrackerHelper } trait MeetingActivityResponseCmdMsgHdlr { @@ -12,15 +12,11 @@ trait MeetingActivityResponseCmdMsgHdlr { val outGW: OutMessageGateway def handleMeetingActivityResponseCmdMsg( - msg: MeetingActivityResponseCmdMsg, - tracker: MeetingInactivityTracker, - helper: MeetingInactivityTrackerHelper - ): MeetingInactivityTracker = { - helper.processMeetingActivityResponse( - props = liveMeeting.props, - outGW, - msg, - tracker - ) + msg: MeetingActivityResponseCmdMsg, + state: MeetingState2x, + helper: MeetingInactivityTrackerHelper + ): MeetingState2x = { + helper.processMeetingActivityResponse(liveMeeting.props, outGW, msg) + MeetingInactivityTracker.resetWarningSentAndTimestamp(state) } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala index 7bc3c936cb..bb966a4469 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserLeaveReqMsgHdlr.scala @@ -2,8 +2,10 @@ package org.bigbluebutton.core.apps.users import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway +import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x } import org.bigbluebutton.core.models.Users2x import org.bigbluebutton.core.running.MeetingActor +import org.bigbluebutton.core.util.TimeUtil import org.bigbluebutton.core2.message.senders.MsgBuilder trait UserLeaveReqMsgHdlr { @@ -11,20 +13,26 @@ trait UserLeaveReqMsgHdlr { val outGW: OutMessageGateway - def handleUserLeaveReqMsg(msg: UserLeaveReqMsg): Unit = { + def handleUserLeaveReqMsg(msg: UserLeaveReqMsg, state: MeetingState2x): MeetingState2x = { for { u <- Users2x.remove(liveMeeting.users2x, msg.body.userId) } yield { log.info("User left meeting. meetingId=" + props.meetingProp.intId + " userId=" + u.intId + " user=" + u) captionApp2x.handleUserLeavingMsg(msg.body.userId) - liveMeeting.startCheckingIfWeNeedToEndVoiceConf() stopAutoStartedRecording() // send a user left event for the clients to update val userLeftMeetingEvent = MsgBuilder.buildUserLeftMeetingEvtMsg(liveMeeting.props.meetingProp.intId, u.intId) outGW.send(userLeftMeetingEvent) log.info("User left meetingId=" + liveMeeting.props.meetingProp.intId + " userId=" + msg.body.userId) + + } + + if (Users2x.numUsers(liveMeeting.users2x) == 0) { + MeetingExpiryTracker.setLastUserLeftOn(state, TimeUtil.timeNowInSeconds()) + } else { + state } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala index 2d8aab97de..93de876681 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala @@ -1,11 +1,91 @@ package org.bigbluebutton.core.domain +import com.softwaremill.quicklens._ +import org.bigbluebutton.core.util.TimeUtil + +object MeetingInactivityTracker { + + def warningHasBeenSent(state: MeetingState2x): Boolean = { + state.inactivityTracker.warningSent + } + + def setWarningSentAndTimestamp(state: MeetingState2x, nowInSeconds: Long): MeetingState2x = { + val tracker = state.inactivityTracker.modify(_.warningSent).setTo(true) + .modify(_.warningSentOnTimestamp).setTo(nowInSeconds) + state.modify(_.inactivityTracker).setTo(tracker) + } + + def resetWarningSentAndTimestamp(state: MeetingState2x): MeetingState2x = { + val tracker = state.inactivityTracker.modify(_.warningSent).setTo(false) + .modify(_.warningSentOnTimestamp).setTo(0L) + state.modify(_.inactivityTracker).setTo(tracker) + } + + def updateLastActivityTimestamp(state: MeetingState2x, nowInSeconds: Long): MeetingState2x = { + val tracker = state.inactivityTracker.modify(_.lastActivityTimestamp).setTo(nowInSeconds) + state.modify(_.inactivityTracker).setTo(tracker) + } + + def hasRecentActivity(state: MeetingState2x, nowInSeconds: Long): Boolean = { + nowInSeconds - state.inactivityTracker.lastActivityTimestamp < + TimeUtil.minutesToSeconds(state.inactivityTracker.maxInactivityTimeoutMinutes) + } + + def isMeetingInactive(state: MeetingState2x, nowInSeconds: Long): Boolean = { + state.inactivityTracker.warningSent && + (nowInSeconds - state.inactivityTracker.lastActivityTimestamp) > + TimeUtil.minutesToSeconds(state.inactivityTracker.maxInactivityTimeoutMinutes) + } + + def timeLeftInSeconds(state: MeetingState2x, nowInSeconds: Long): Long = { + state.inactivityTracker.lastActivityTimestamp + + TimeUtil.minutesToSeconds(state.inactivityTracker.maxInactivityTimeoutMinutes) - nowInSeconds + } +} + case class MeetingInactivityTracker( - maxInactivityTimeoutMinutes: Int, - warningMinutesBeforeMax: Int, - lastActivityTimeInMinutes: Long, - warningSent: Boolean, - warningSentOnTimeInMinutes: Long + val maxInactivityTimeoutMinutes: Int, + val warningMinutesBeforeMax: Int, + lastActivityTimestamp: Long, + warningSent: Boolean, + warningSentOnTimestamp: Long ) -case class MeetingExpiryTracker(startedOnInMinutes: Long, meetingJoined: Boolean, lastUserLeftOn: Long) +object MeetingExpiryTracker { + def setMeetingStartedOn(state: MeetingState2x, timestampInSeconds: Long): MeetingState2x = { + val tracker = state.expiryTracker.modify(_.startedOn).setTo(timestampInSeconds) + state.modify(_.expiryTracker).setTo(tracker) + } + + def setUserHasJoined(state: MeetingState2x): MeetingState2x = { + state.expiryTracker.modify(_.userHasJoined).setTo(true) + state + } + + def setLastUserLeftOn(state: MeetingState2x, timestampInSeconds: Long): MeetingState2x = { + state.expiryTracker.modify(_.lastUserLeftOn).setTo(timestampInSeconds) + state + } + + def hasMeetingExpiredNeverBeenJoined(state: MeetingState2x, nowInSeconds: Long): Boolean = { + nowInSeconds - state.expiryTracker.startedOn > + TimeUtil.minutesToSeconds(state.expiryTracker.meetingExpireIfNoUserJoinedInMinutes) + } + + def meetingOverDuration(state: MeetingState2x, nowInSeconds: Long): Boolean = { + nowInSeconds > state.expiryTracker.startedOn + TimeUtil.minutesToSeconds(state.expiryTracker.durationInMinutes) + } + + def endMeetingTime(state: MeetingState2x): Int = { + (state.expiryTracker.startedOn + TimeUtil.minutesToSeconds(state.expiryTracker.durationInMinutes)).toInt + } +} + +case class MeetingExpiryTracker( + startedOn: Long, + userHasJoined: Boolean, + lastUserLeftOn: Long, + durationInMinutes: Int, + meetingExpireIfNoUserJoinedInMinutes: Int, + meetingExpireWhenLastUserLeftInMinutes: Int +) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingState2x.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingState2x.scala new file mode 100755 index 0000000000..f86cedfdd7 --- /dev/null +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingState2x.scala @@ -0,0 +1,11 @@ +package org.bigbluebutton.core.domain + +object MeetingState2x { + +} + +case class MeetingState2x( + inactivityTracker: MeetingInactivityTracker, + expiryTracker: MeetingExpiryTracker +) + diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala index b7fca04e41..9534b2d436 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala @@ -1,212 +1,225 @@ -package org.bigbluebutton.core.running - -import org.bigbluebutton.SystemConfiguration -import org.bigbluebutton.common2.msgs._ -import org.bigbluebutton.core.api.{ BreakoutRoomEndedInternalMsg, DestroyMeetingInternalMsg, RecordingStatusChanged } -import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus } -import org.bigbluebutton.core.{ MessageRecorder, OutMessageGateway } -import org.bigbluebutton.core.models._ -import org.bigbluebutton.core2.MeetingStatus2x -import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender, UserJoinedMeetingEvtMsgBuilder } - -trait HandlerHelpers extends SystemConfiguration { - - def validateTokenFailed(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String, - valid: Boolean, waitForApproval: Boolean): Unit = { - val event = MsgBuilder.buildValidateAuthTokenRespMsg( - meetingId, - userId, authToken, valid, waitForApproval - ) - Sender.send(outGW, event) - - // TODO: Should disconnect user here. - } - - def sendValidateAuthTokenRespMsg(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String, - valid: Boolean, waitForApproval: Boolean): Unit = { - val event = MsgBuilder.buildValidateAuthTokenRespMsg( - meetingId, - userId, authToken, valid, waitForApproval - ) - Sender.send(outGW, event) - } - - def userValidatedButNeedToWaitForApproval(outGW: OutMessageGateway, liveMeeting: LiveMeeting, - user: RegisteredUser): Unit = { - val meetingId = liveMeeting.props.meetingProp.intId - sendValidateAuthTokenRespMsg(outGW, meetingId, user.id, user.authToken, valid = true, waitForApproval = false) - - val guest = GuestWaiting(user.id, user.name, user.role) - addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting) - notifyModeratorsOfGuestWaiting(outGW, Vector(guest), liveMeeting.users2x, meetingId) - } - - def addGuestToWaitingForApproval(guest: GuestWaiting, guestsWaitingList: GuestsWaiting): Unit = { - GuestsWaiting.add(guestsWaitingList, guest) - } - - def userValidatedAndNoNeedToWaitForApproval( - outGW: OutMessageGateway, - liveMeeting: LiveMeeting, - user: RegisteredUser - ): Unit = { - - println("**************** userValidatedAndNoNeedToWaitForApproval") - - val meetingId = liveMeeting.props.meetingProp.intId - sendValidateAuthTokenRespMsg(outGW, meetingId, - userId = user.id, authToken = user.authToken, valid = true, waitForApproval = false) - - // TODO: REMOVE Temp only so we can implement user handling in client. (ralam june 21, 2017) - - sendAllUsersInMeeting(outGW, user.id, liveMeeting) - sendAllVoiceUsersInMeeting(outGW, user.id, liveMeeting.voiceUsers, meetingId) - sendAllWebcamStreams(outGW, user.id, liveMeeting.webcams, meetingId) - userJoinMeeting(outGW, user.authToken, liveMeeting) - if (!Users2x.hasPresenter(liveMeeting.users2x)) { - automaticallyAssignPresenter(outGW, liveMeeting) - } - } - - def notifyModeratorsOfGuestWaiting(outGW: OutMessageGateway, guests: Vector[GuestWaiting], users: Users2x, meetingId: String): Unit = { - val mods = Users2x.findAll(users).filter(p => p.role == Roles.MODERATOR_ROLE) - mods foreach { m => - val event = MsgBuilder.buildGuestsWaitingForApprovalEvtMsg(meetingId, m.intId, guests) - Sender.send(outGW, event) - } - } - - def sendAllUsersInMeeting(outGW: OutMessageGateway, requesterId: String, liveMeeting: LiveMeeting): Unit = { - val meetingId = liveMeeting.props.meetingProp.intId - val users = Users2x.findAll(liveMeeting.users2x) - val webUsers = users.map { u => - WebUser(intId = u.intId, extId = u.extId, name = u.name, role = u.role, - guest = u.guest, authed = u.authed, waitingForAcceptance = u.waitingForAcceptance, emoji = u.emoji, - locked = u.locked, presenter = u.presenter, avatar = u.avatar) - } - - val event = MsgBuilder.buildGetUsersMeetingRespMsg(meetingId, requesterId, webUsers) - Sender.send(outGW, event) - } - - def sendAllWebcamStreams(outGW: OutMessageGateway, requesterId: String, webcams: Webcams, meetingId: String): Unit = { - val streams = org.bigbluebutton.core.models.Webcams.findAll(webcams) - val webcamStreams = streams.map { u => - val msVO = MediaStreamVO(id = u.stream.id, url = u.stream.url, userId = u.stream.userId, - attributes = u.stream.attributes, viewers = u.stream.viewers) - - WebcamStreamVO(streamId = msVO.id, stream = msVO) - } - - val event = MsgBuilder.buildGetWebcamStreamsMeetingRespMsg(meetingId, requesterId, webcamStreams) - Sender.send(outGW, event) - } - - def sendAllVoiceUsersInMeeting(outGW: OutMessageGateway, requesterId: String, - voiceUsers: VoiceUsers, - meetingId: String): Unit = { - - val vu = VoiceUsers.findAll(voiceUsers).map { u => - VoiceConfUser(intId = u.intId, voiceUserId = u.voiceUserId, callingWith = u.callingWith, callerName = u.callerName, - callerNum = u.callerNum, muted = u.muted, talking = u.talking, listenOnly = u.listenOnly) - } - - val event = MsgBuilder.buildGetVoiceUsersMeetingRespMsg(meetingId, requesterId, vu) - Sender.send(outGW, event) - } - - def userJoinMeeting(outGW: OutMessageGateway, authToken: String, liveMeeting: LiveMeeting): Unit = { - for { - regUser <- RegisteredUsers.findWithToken(authToken, liveMeeting.registeredUsers) - } yield { - val userState = UserState( - intId = regUser.id, - extId = regUser.externId, - name = regUser.name, - role = regUser.role, - guest = regUser.guest, - authed = regUser.authed, - waitingForAcceptance = regUser.waitingForAcceptance, - emoji = "none", - presenter = false, - locked = false, - avatar = regUser.avatarURL - ) - - Users2x.add(liveMeeting.users2x, userState) - - val event = UserJoinedMeetingEvtMsgBuilder.build(liveMeeting.props.meetingProp.intId, userState) - Sender.send(outGW, event) - - startRecordingIfAutoStart2x(liveMeeting) - } - } - - def startRecordingIfAutoStart2x(liveMeeting: LiveMeeting): Unit = { - if (liveMeeting.props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) && - liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) { - - MeetingStatus2x.recordingStarted(liveMeeting.status) - // outGW.send(new RecordingStatusChanged(props.meetingProp.intId, props.recordProp.record, - // "system", MeetingStatus2x.isRecording(liveMeeting.status))) - } - } - - def automaticallyAssignPresenter(outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = { - val meetingId = liveMeeting.props.meetingProp.intId - for { - moderator <- Users2x.findModerator(liveMeeting.users2x) - newPresenter <- Users2x.makePresenter(liveMeeting.users2x, moderator.intId) - } yield { - sendPresenterAssigned(outGW, meetingId, newPresenter.intId, newPresenter.name, newPresenter.name) - } - } - - def sendPresenterAssigned(outGW: OutMessageGateway, meetingId: String, intId: String, name: String, assignedBy: String): Unit = { - def event = MsgBuilder.buildPresenterAssignedEvtMsg(meetingId, intId, name, assignedBy) - outGW.send(event) - } - - def endMeeting(outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = { - def buildMeetingEndingEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") - val envelope = BbbCoreEnvelope(MeetingEndingEvtMsg.NAME, routing) - val body = MeetingEndingEvtMsgBody(meetingId) - val header = BbbClientMsgHeader(MeetingEndingEvtMsg.NAME, meetingId, "not-used") - val event = MeetingEndingEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - val endingEvent = buildMeetingEndingEvtMsg(liveMeeting.props.meetingProp.intId) - - // Broadcast users the meeting will end - outGW.send(endingEvent) - - MeetingStatus2x.meetingHasEnded(liveMeeting.status) - - def buildMeetingEndedEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { - val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") - val envelope = BbbCoreEnvelope(MeetingEndedEvtMsg.NAME, routing) - val body = MeetingEndedEvtMsgBody(meetingId) - val header = BbbCoreBaseHeader(MeetingEndedEvtMsg.NAME) - val event = MeetingEndedEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - val endedEvnt = buildMeetingEndedEvtMsg(liveMeeting.props.meetingProp.intId) - outGW.send(endedEvnt) - } - - def destroyMeeting(eventBus: IncomingEventBus, meetingId: String): Unit = { - eventBus.publish(BigBlueButtonEvent(meetingManagerChannel, new DestroyMeetingInternalMsg(meetingId))) - } - - def notifyParentThatBreakoutEnded(eventBus: IncomingEventBus, liveMeeting: LiveMeeting): Unit = { - eventBus.publish(BigBlueButtonEvent( - liveMeeting.props.breakoutProps.parentId, - new BreakoutRoomEndedInternalMsg(liveMeeting.props.meetingProp.intId) - )) - } -} +package org.bigbluebutton.core.running + +import org.bigbluebutton.SystemConfiguration +import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.core.api.{ BreakoutRoomEndedInternalMsg, DestroyMeetingInternalMsg, RecordingStatusChanged } +import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus } +import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x } +import org.bigbluebutton.core.{ MessageRecorder, OutMessageGateway } +import org.bigbluebutton.core.models._ +import org.bigbluebutton.core2.MeetingStatus2x +import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender, UserJoinedMeetingEvtMsgBuilder } + +trait HandlerHelpers extends SystemConfiguration { + + def validateTokenFailed(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String, + valid: Boolean, waitForApproval: Boolean): Unit = { + val event = MsgBuilder.buildValidateAuthTokenRespMsg( + meetingId, + userId, authToken, valid, waitForApproval + ) + Sender.send(outGW, event) + + // TODO: Should disconnect user here. + } + + def sendValidateAuthTokenRespMsg(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String, + valid: Boolean, waitForApproval: Boolean): Unit = { + val event = MsgBuilder.buildValidateAuthTokenRespMsg( + meetingId, + userId, authToken, valid, waitForApproval + ) + Sender.send(outGW, event) + } + + def userValidatedButNeedToWaitForApproval(outGW: OutMessageGateway, liveMeeting: LiveMeeting, + user: RegisteredUser): Unit = { + val meetingId = liveMeeting.props.meetingProp.intId + sendValidateAuthTokenRespMsg(outGW, meetingId, user.id, user.authToken, valid = true, waitForApproval = false) + + val guest = GuestWaiting(user.id, user.name, user.role) + addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting) + notifyModeratorsOfGuestWaiting(outGW, Vector(guest), liveMeeting.users2x, meetingId) + } + + def addGuestToWaitingForApproval(guest: GuestWaiting, guestsWaitingList: GuestsWaiting): Unit = { + GuestsWaiting.add(guestsWaitingList, guest) + } + + def userValidatedAndNoNeedToWaitForApproval( + outGW: OutMessageGateway, + liveMeeting: LiveMeeting, + user: RegisteredUser, + state: MeetingState2x + ): MeetingState2x = { + + val meetingId = liveMeeting.props.meetingProp.intId + sendValidateAuthTokenRespMsg(outGW, meetingId, + userId = user.id, authToken = user.authToken, valid = true, waitForApproval = false) + + // TODO: REMOVE Temp only so we can implement user handling in client. (ralam june 21, 2017) + + sendAllUsersInMeeting(outGW, user.id, liveMeeting) + sendAllVoiceUsersInMeeting(outGW, user.id, liveMeeting.voiceUsers, meetingId) + sendAllWebcamStreams(outGW, user.id, liveMeeting.webcams, meetingId) + val newState = userJoinMeeting(outGW, user.authToken, liveMeeting, state) + if (!Users2x.hasPresenter(liveMeeting.users2x)) { + automaticallyAssignPresenter(outGW, liveMeeting) + } + newState + } + + def notifyModeratorsOfGuestWaiting(outGW: OutMessageGateway, guests: Vector[GuestWaiting], users: Users2x, meetingId: String): Unit = { + val mods = Users2x.findAll(users).filter(p => p.role == Roles.MODERATOR_ROLE) + mods foreach { m => + val event = MsgBuilder.buildGuestsWaitingForApprovalEvtMsg(meetingId, m.intId, guests) + Sender.send(outGW, event) + } + } + + def sendAllUsersInMeeting(outGW: OutMessageGateway, requesterId: String, liveMeeting: LiveMeeting): Unit = { + val meetingId = liveMeeting.props.meetingProp.intId + val users = Users2x.findAll(liveMeeting.users2x) + val webUsers = users.map { u => + WebUser(intId = u.intId, extId = u.extId, name = u.name, role = u.role, + guest = u.guest, authed = u.authed, waitingForAcceptance = u.waitingForAcceptance, emoji = u.emoji, + locked = u.locked, presenter = u.presenter, avatar = u.avatar) + } + + val event = MsgBuilder.buildGetUsersMeetingRespMsg(meetingId, requesterId, webUsers) + Sender.send(outGW, event) + } + + def sendAllWebcamStreams(outGW: OutMessageGateway, requesterId: String, webcams: Webcams, meetingId: String): Unit = { + val streams = org.bigbluebutton.core.models.Webcams.findAll(webcams) + val webcamStreams = streams.map { u => + val msVO = MediaStreamVO(id = u.stream.id, url = u.stream.url, userId = u.stream.userId, + attributes = u.stream.attributes, viewers = u.stream.viewers) + + WebcamStreamVO(streamId = msVO.id, stream = msVO) + } + + val event = MsgBuilder.buildGetWebcamStreamsMeetingRespMsg(meetingId, requesterId, webcamStreams) + Sender.send(outGW, event) + } + + def sendAllVoiceUsersInMeeting(outGW: OutMessageGateway, requesterId: String, + voiceUsers: VoiceUsers, + meetingId: String): Unit = { + + val vu = VoiceUsers.findAll(voiceUsers).map { u => + VoiceConfUser(intId = u.intId, voiceUserId = u.voiceUserId, callingWith = u.callingWith, callerName = u.callerName, + callerNum = u.callerNum, muted = u.muted, talking = u.talking, listenOnly = u.listenOnly) + } + + val event = MsgBuilder.buildGetVoiceUsersMeetingRespMsg(meetingId, requesterId, vu) + Sender.send(outGW, event) + } + + def userJoinMeeting(outGW: OutMessageGateway, authToken: String, + liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = { + val nu = for { + regUser <- RegisteredUsers.findWithToken(authToken, liveMeeting.registeredUsers) + } yield { + UserState( + intId = regUser.id, + extId = regUser.externId, + name = regUser.name, + role = regUser.role, + guest = regUser.guest, + authed = regUser.authed, + waitingForAcceptance = regUser.waitingForAcceptance, + emoji = "none", + presenter = false, + locked = false, + avatar = regUser.avatarURL + ) + } + + nu match { + case Some(newUser) => + Users2x.add(liveMeeting.users2x, newUser) + + val event = UserJoinedMeetingEvtMsgBuilder.build(liveMeeting.props.meetingProp.intId, newUser) + Sender.send(outGW, event) + startRecordingIfAutoStart2x(liveMeeting) + + if (!state.expiryTracker.userHasJoined) { + MeetingExpiryTracker.setUserHasJoined(state) + } else { + state + } + + case None => + state + } + } + + def startRecordingIfAutoStart2x(liveMeeting: LiveMeeting): Unit = { + if (liveMeeting.props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) && + liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) { + + MeetingStatus2x.recordingStarted(liveMeeting.status) + // outGW.send(new RecordingStatusChanged(props.meetingProp.intId, props.recordProp.record, + // "system", MeetingStatus2x.isRecording(liveMeeting.status))) + } + } + + def automaticallyAssignPresenter(outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = { + val meetingId = liveMeeting.props.meetingProp.intId + for { + moderator <- Users2x.findModerator(liveMeeting.users2x) + newPresenter <- Users2x.makePresenter(liveMeeting.users2x, moderator.intId) + } yield { + sendPresenterAssigned(outGW, meetingId, newPresenter.intId, newPresenter.name, newPresenter.name) + } + } + + def sendPresenterAssigned(outGW: OutMessageGateway, meetingId: String, intId: String, name: String, assignedBy: String): Unit = { + def event = MsgBuilder.buildPresenterAssignedEvtMsg(meetingId, intId, name, assignedBy) + outGW.send(event) + } + + def endMeeting(outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = { + def buildMeetingEndingEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") + val envelope = BbbCoreEnvelope(MeetingEndingEvtMsg.NAME, routing) + val body = MeetingEndingEvtMsgBody(meetingId) + val header = BbbClientMsgHeader(MeetingEndingEvtMsg.NAME, meetingId, "not-used") + val event = MeetingEndingEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + val endingEvent = buildMeetingEndingEvtMsg(liveMeeting.props.meetingProp.intId) + + // Broadcast users the meeting will end + outGW.send(endingEvent) + + MeetingStatus2x.meetingHasEnded(liveMeeting.status) + + def buildMeetingEndedEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { + val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") + val envelope = BbbCoreEnvelope(MeetingEndedEvtMsg.NAME, routing) + val body = MeetingEndedEvtMsgBody(meetingId) + val header = BbbCoreBaseHeader(MeetingEndedEvtMsg.NAME) + val event = MeetingEndedEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + val endedEvnt = buildMeetingEndedEvtMsg(liveMeeting.props.meetingProp.intId) + outGW.send(endedEvnt) + } + + def destroyMeeting(eventBus: IncomingEventBus, meetingId: String): Unit = { + eventBus.publish(BigBlueButtonEvent(meetingManagerChannel, new DestroyMeetingInternalMsg(meetingId))) + } + + def notifyParentThatBreakoutEnded(eventBus: IncomingEventBus, liveMeeting: LiveMeeting): Unit = { + eventBus.publish(BigBlueButtonEvent( + liveMeeting.props.breakoutProps.parentId, + new BreakoutRoomEndedInternalMsg(liveMeeting.props.meetingProp.intId) + )) + } +} diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/LiveMeeting.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/LiveMeeting.scala index 9b1a1ddf76..55a5118060 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/LiveMeeting.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/LiveMeeting.scala @@ -5,7 +5,6 @@ import java.util.concurrent.TimeUnit import org.bigbluebutton.common2.domain.DefaultProps import org.bigbluebutton.core.api._ import org.bigbluebutton.core.apps._ -import org.bigbluebutton.core.domain.Meeting3x import org.bigbluebutton.core.models._ import org.bigbluebutton.core2.MeetingStatus2x @@ -33,28 +32,6 @@ class LiveMeeting( MeetingStatus2x.hasMeetingEnded(status) } - def startCheckingIfWeNeedToEndVoiceConf() { - if (Users2x.numUsers(users2x) == 0 && !props.meetingProp.isBreakout) { - MeetingStatus2x.lastWebUserLeft(status) - } - } - - def sendTimeRemainingNotice() { - val now = timeNowInSeconds - - if (props.durationProps.duration > 0 && (((MeetingStatus2x.startedOn(status) + props.durationProps.duration) - now) < 15)) { - // log.warning("MEETING WILL END IN 15 MINUTES!!!!") - } - } - - def timeNowInMinutes(): Long = { - TimeUnit.NANOSECONDS.toMinutes(System.nanoTime()) - } - - def timeNowInSeconds(): Long = { - TimeUnit.NANOSECONDS.toSeconds(System.nanoTime()) - } - def lockLayout(lock: Boolean) { MeetingStatus2x.lockLayout(status, lock) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala index 2b6efddad5..c83dc77699 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala @@ -1,438 +1,429 @@ -package org.bigbluebutton.core.running - -import java.io.{ PrintWriter, StringWriter } - -import org.bigbluebutton.core.apps.users.UsersApp -import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingInactivityTracker } -import org.bigbluebutton.core.util.TimeUtil -//import java.util.concurrent.TimeUnit - -import akka.actor._ -import akka.actor.SupervisorStrategy.Resume -import org.bigbluebutton.common2.domain.DefaultProps -import org.bigbluebutton.core._ -import org.bigbluebutton.core.api._ -import org.bigbluebutton.core.apps._ -import org.bigbluebutton.core.apps.caption.CaptionApp2x -import org.bigbluebutton.core.apps.chat.ChatApp2x -import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x -import org.bigbluebutton.core.apps.presentation.PresentationApp2x -import org.bigbluebutton.core.apps.meeting._ -import org.bigbluebutton.core.apps.users.UsersApp2x -import org.bigbluebutton.core.apps.sharednotes.SharedNotesApp2x -import org.bigbluebutton.core.apps.whiteboard.WhiteboardApp2x -import org.bigbluebutton.core.bus._ -import org.bigbluebutton.core.models._ -import org.bigbluebutton.core2.MeetingStatus2x -import org.bigbluebutton.core2.message.handlers._ -import org.bigbluebutton.core2.message.handlers.users._ -import org.bigbluebutton.core2.message.handlers.meeting._ -import org.bigbluebutton.common2.msgs._ -import org.bigbluebutton.core.apps.breakout._ -import org.bigbluebutton.core.apps.polls._ -import org.bigbluebutton.core.apps.voice._ - -import scala.concurrent.duration._ -import org.bigbluebutton.core2.testdata.FakeTestData -import org.bigbluebutton.core.apps.layout.LayoutApp2x -import org.bigbluebutton.core.apps.meeting.SyncGetMeetingInfoRespMsgHdlr - -object MeetingActor { - def props( - props: DefaultProps, - eventBus: IncomingEventBus, - outGW: OutMessageGateway, - liveMeeting: LiveMeeting - ): Props = - Props(classOf[MeetingActor], props, eventBus, outGW, liveMeeting) -} - -class MeetingActor( - val props: DefaultProps, - val eventBus: IncomingEventBus, - val outGW: OutMessageGateway, - val liveMeeting: LiveMeeting -) - extends BaseMeetingActor - with GuestsApp - with LayoutApp2x - with VoiceApp2x - with PollApp2x - with BreakoutApp2x - with UsersApp2x - with WhiteboardApp2x - - with PermisssionCheck - with UserBroadcastCamStartMsgHdlr - with UserJoinMeetingReqMsgHdlr - with UserBroadcastCamStopMsgHdlr - with UserConnectedToGlobalAudioMsgHdlr - with UserDisconnectedFromGlobalAudioMsgHdlr - with MuteAllExceptPresentersCmdMsgHdlr - with MuteMeetingCmdMsgHdlr - with IsMeetingMutedReqMsgHdlr - with MuteUserCmdMsgHdlr - with EjectUserFromVoiceCmdMsgHdlr - with EndMeetingSysCmdMsgHdlr - with DestroyMeetingSysCmdMsgHdlr - with SendTimeRemainingUpdateHdlr - with SyncGetMeetingInfoRespMsgHdlr { - - override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) { - case e: Exception => { - val sw: StringWriter = new StringWriter() - sw.write("An exception has been thrown on MeetingActor, exception message [" + e.getMessage() + "] (full stacktrace below)\n") - e.printStackTrace(new PrintWriter(sw)) - log.error(sw.toString()) - Resume - } - } - - /** - * Put the internal message injector into another actor so this - * actor is easy to test. - */ - var actorMonitor = context.actorOf( - MeetingActorAudit.props(props, eventBus, outGW), - "actorMonitor-" + props.meetingProp.intId - ) - - val presentationApp2x = new PresentationApp2x(liveMeeting, outGW) - val screenshareApp2x = new ScreenshareApp2x(liveMeeting, outGW) - val captionApp2x = new CaptionApp2x(liveMeeting, outGW) - val sharedNotesApp2x = new SharedNotesApp2x(liveMeeting, outGW) - val chatApp2x = new ChatApp2x(liveMeeting, outGW) - val usersApp = new UsersApp(liveMeeting, outGW, eventBus) - - var inactivityTracker = new MeetingInactivityTracker( - liveMeeting.props.durationProps.maxInactivityTimeoutMinutes, - liveMeeting.props.durationProps.warnMinutesBeforeMax, - TimeUtil.millisToMinutes(System.currentTimeMillis()), false, 0L - ) - - val inactivityTrackerHelper = new MeetingInactivityTrackerHelper(liveMeeting, outGW) - - var expiryTracker = new MeetingExpiryTracker( - TimeUtil.millisToMinutes(System.currentTimeMillis()), - false, 0L - ) - - val expiryTrackerHelper = new MeetingExpiryTrackerHelper(liveMeeting, outGW) - - /*******************************************************************/ - //object FakeTestData extends FakeTestData - //FakeTestData.createFakeUsers(liveMeeting) - /*******************************************************************/ - - def receive = { - //============================= - // 2x messages - 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) - - // Meeting - case m: DestroyMeetingSysCmdMsg => handleDestroyMeetingSysCmdMsg(m) - - //====================================== - - //======================================= - // old messages - case msg: MonitorNumberOfUsersInternalMsg => handleMonitorNumberOfUsers(msg) - - case msg: AllowUserToShareDesktop => handleAllowUserToShareDesktop(msg) - case msg: ExtendMeetingDuration => handleExtendMeetingDuration(msg) - case msg: SendTimeRemainingUpdate => handleSendTimeRemainingUpdate(msg) - - // Screenshare - case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg) - - // Guest - case msg: GetGuestPolicy => handleGetGuestPolicy(msg) - case msg: SetGuestPolicy => handleSetGuestPolicy(msg) - - case _ => // do nothing - } - - private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = { - // TODO: Update meeting activity status here - // updateActivityStatus(msg) - - msg.core match { - // Users - case m: ValidateAuthTokenReqMsg => usersApp.handleValidateAuthTokenReqMsg(m) - case m: UserJoinMeetingReqMsg => handleUserJoinMeetingReqMsg(m) - case m: UserLeaveReqMsg => handleUserLeaveReqMsg(m) - case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m) - case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m) - case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m) - case m: MeetingActivityResponseCmdMsg => - inactivityTracker = usersApp.handleMeetingActivityResponseCmdMsg(m, inactivityTracker, inactivityTrackerHelper) - case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m) - case m: SetRecordingStatusCmdMsg => usersApp.handleSetRecordingStatusCmdMsg(m) - case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m) - case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m) - case m: EjectUserFromMeetingCmdMsg => usersApp.handleEjectUserFromMeetingCmdMsg(m) - case m: GetUsersMeetingReqMsg => usersApp.handleGetUsersMeetingReqMsg(m) - - // Whiteboard - case m: SendCursorPositionPubMsg => handleSendCursorPositionPubMsg(m) - case m: ClearWhiteboardPubMsg => handleClearWhiteboardPubMsg(m) - case m: UndoWhiteboardPubMsg => handleUndoWhiteboardPubMsg(m) - case m: ModifyWhiteboardAccessPubMsg => handleModifyWhiteboardAccessPubMsg(m) - case m: GetWhiteboardAccessReqMsg => handleGetWhiteboardAccessReqMsg(m) - case m: SendWhiteboardAnnotationPubMsg => handleSendWhiteboardAnnotationPubMsg(m) - case m: GetWhiteboardAnnotationsReqMsg => handleGetWhiteboardAnnotationsReqMsg(m) - - // Poll - case m: StartPollReqMsg => handleStartPollReqMsg(m) - case m: StartCustomPollReqMsg => handleStartCustomPollReqMsg(m) - case m: StopPollReqMsg => handleStopPollReqMsg(m) - case m: ShowPollResultReqMsg => handleShowPollResultReqMsg(m) - case m: HidePollResultReqMsg => handleHidePollResultReqMsg(m) - case m: GetCurrentPollReqMsg => handleGetCurrentPollReqMsg(m) - case m: RespondToPollReqMsg => handleRespondToPollReqMsg(m) - - // Breakout - case m: BreakoutRoomsListMsg => handleBreakoutRoomsListMsg(m) - case m: CreateBreakoutRoomsCmdMsg => handleCreateBreakoutRoomsCmdMsg(m) - case m: EndAllBreakoutRoomsMsg => handleEndAllBreakoutRoomsMsg(m) - case m: RequestBreakoutJoinURLReqMsg => handleRequestBreakoutJoinURLReqMsg(m) - case m: BreakoutRoomCreatedMsg => handleBreakoutRoomCreatedMsg(m) - case m: BreakoutRoomEndedMsg => handleBreakoutRoomEndedMsg(m) - case m: BreakoutRoomUsersUpdateMsg => handleBreakoutRoomUsersUpdateMsg(m) - case m: SendBreakoutUsersUpdateMsg => handleSendBreakoutUsersUpdateMsg(m) - case m: TransferUserToMeetingRequestMsg => handleTransferUserToMeetingRequestMsg(m) - - // Voice - case m: UserLeftVoiceConfEvtMsg => handleUserLeftVoiceConfEvtMsg(m) - case m: UserMutedInVoiceConfEvtMsg => handleUserMutedInVoiceConfEvtMsg(m) - case m: UserTalkingInVoiceConfEvtMsg => handleUserTalkingInVoiceConfEvtMsg(m) - case m: RecordingStartedVoiceConfEvtMsg => handleRecordingStartedVoiceConfEvtMsg(m) - case m: MuteUserCmdMsg => handleMuteUserCmdMsg(m) - case m: MuteAllExceptPresentersCmdMsg => handleMuteAllExceptPresentersCmdMsg(m) - case m: EjectUserFromVoiceCmdMsg => handleEjectUserFromVoiceCmdMsg(m) - case m: IsMeetingMutedReqMsg => handleIsMeetingMutedReqMsg(m) - case m: MuteMeetingCmdMsg => handleMuteMeetingCmdMsg(m) - case m: UserConnectedToGlobalAudioMsg => handleUserConnectedToGlobalAudioMsg(m) - case m: UserDisconnectedFromGlobalAudioMsg => handleUserDisconnectedFromGlobalAudioMsg(m) - - // Layout - case m: GetCurrentLayoutReqMsg => handleGetCurrentLayoutReqMsg(m) - case m: LockLayoutMsg => handleLockLayoutMsg(m) - case m: BroadcastLayoutMsg => handleBroadcastLayoutMsg(m) - - // Presentation - case m: SetCurrentPresentationPubMsg => presentationApp2x.handleSetCurrentPresentationPubMsg(m) - case m: GetPresentationInfoReqMsg => presentationApp2x.handleGetPresentationInfoReqMsg(m) - case m: SetCurrentPagePubMsg => presentationApp2x.handleSetCurrentPagePubMsg(m) - case m: ResizeAndMovePagePubMsg => presentationApp2x.handleResizeAndMovePagePubMsg(m) - case m: RemovePresentationPubMsg => presentationApp2x.handleRemovePresentationPubMsg(m) - case m: PreuploadedPresentationsSysPubMsg => presentationApp2x.handlePreuploadedPresentationsPubMsg(m) - case m: PresentationConversionUpdateSysPubMsg => presentationApp2x.handlePresentationConversionUpdatePubMsg(m) - case m: PresentationPageCountErrorSysPubMsg => presentationApp2x.handlePresentationPageCountErrorPubMsg(m) - case m: PresentationPageGeneratedSysPubMsg => presentationApp2x.handlePresentationPageGeneratedPubMsg(m) - case m: PresentationConversionCompletedSysPubMsg => presentationApp2x.handlePresentationConversionCompletedPubMsg(m) - case m: AssignPresenterReqMsg => handlePresenterChange(m) - - // Caption - case m: EditCaptionHistoryPubMsg => captionApp2x.handleEditCaptionHistoryPubMsg(m) - case m: UpdateCaptionOwnerPubMsg => captionApp2x.handleUpdateCaptionOwnerPubMsg(m) - case m: SendCaptionHistoryReqMsg => captionApp2x.handleSendCaptionHistoryReqMsg(m) - - // SharedNotes - case m: GetSharedNotesPubMsg => sharedNotesApp2x.handleGetSharedNotesPubMsg(m) - case m: SyncSharedNotePubMsg => sharedNotesApp2x.handleSyncSharedNotePubMsg(m) - case m: UpdateSharedNoteReqMsg => sharedNotesApp2x.handleUpdateSharedNoteReqMsg(m) - case m: CreateSharedNoteReqMsg => sharedNotesApp2x.handleCreateSharedNoteReqMsg(m) - case m: DestroySharedNoteReqMsg => sharedNotesApp2x.handleDestroySharedNoteReqMsg(m) - - // Guests - case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m) - case m: SetGuestPolicyMsg => handleSetGuestPolicyMsg(m) - case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m) - - // Chat - case m: GetChatHistoryReqMsg => chatApp2x.handleGetChatHistoryReqMsg(m) - case m: SendPublicMessagePubMsg => chatApp2x.handleSendPublicMessagePubMsg(m) - case m: SendPrivateMessagePubMsg => chatApp2x.handleSendPrivateMessagePubMsg(m) - case m: ClearPublicChatHistoryPubMsg => chatApp2x.handleClearPublicChatHistoryPubMsg(m) - - // Screenshare - case m: ScreenshareStartedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareStartedVoiceConfEvtMsg(m) - case m: ScreenshareStoppedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareStoppedVoiceConfEvtMsg(m) - case m: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareRtmpBroadcastStartedVoiceConfEvtMsg(m) - case m: ScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg(m) - - case _ => log.warning("***** Cannot handle " + msg.envelope.name) - } - } - - def handleGetAllMeetingsReqMsg(msg: GetAllMeetingsReqMsg): Unit = { - // sync all meetings - handleSyncGetMeetingInfoRespMsg(liveMeeting.props) - - // sync all users - usersApp.handleSyncGetUsersMeetingRespMsg() - - // sync all presentations - presentationApp2x.handleSyncGetPresentationInfoRespMsg() - - // TODO send all chat - // TODO send all lock settings - // TODO send all screen sharing info - } - - def handlePresenterChange(msg: AssignPresenterReqMsg): Unit = { - // Stop poll if one is running as presenter left - handleStopPollReqMsg(msg.header.userId) - - // switch user presenter status for old and new presenter - usersApp.handleAssignPresenterReqMsg(msg) - - // TODO stop current screen sharing session (initiated by the old presenter) - - } - - def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = { - - log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting=" - + ScreenshareModel.isBroadcastingRTMP(liveMeeting.screenshareModel) + " URL:" + - ScreenshareModel.getRTMPBroadcastingUrl(liveMeeting.screenshareModel)) - - if (ScreenshareModel.isBroadcastingRTMP(liveMeeting.screenshareModel)) { - // if the meeting has an ongoing WebRTC Deskshare session, send a notification - //outGW.send(new DeskShareNotifyASingleViewer(props.meetingProp.intId, msg.requesterID, - // DeskshareModel.getRTMPBroadcastingUrl(liveMeeting.deskshareModel), - // DeskshareModel.getDesktopShareVideoWidth(liveMeeting.deskshareModel), - // DeskshareModel.getDesktopShareVideoHeight(liveMeeting.deskshareModel), true)) - } - } - - def handleGetGuestPolicy(msg: GetGuestPolicy) { - // outGW.send(new GetGuestPolicyReply(msg.meetingID, props.recordProp.record, - // msg.requesterID, MeetingStatus2x.getGuestPolicy(liveMeeting.status).toString())) - } - - def handleSetGuestPolicy(msg: SetGuestPolicy) { - // 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 handleAllowUserToShareDesktop(msg: AllowUserToShareDesktop): Unit = { - Users2x.findPresenter(liveMeeting.users2x) match { - case Some(curPres) => { - val allowed = msg.userID equals (curPres.intId) - // outGW.send(AllowUserToShareDesktopOut(msg.meetingID, msg.userID, allowed)) - } - case None => // do nothing - } - } - - def handleMonitorNumberOfUsers(msg: MonitorNumberOfUsersInternalMsg) { - inactivityTracker = inactivityTrackerHelper.processMeetingInactivityAudit( - props = liveMeeting.props, - outGW, - eventBus, - inactivityTracker - ) - - expiryTracker = expiryTrackerHelper.processMeetingExpiryAudit(liveMeeting.props, expiryTracker, eventBus) - - monitorNumberOfWebUsers() - monitorNumberOfUsers() - } - - def monitorNumberOfWebUsers() { - - def buildEjectAllFromVoiceConfMsg(meetingId: String, voiceConf: String): BbbCommonEnvCoreMsg = { - val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") - val envelope = BbbCoreEnvelope(EjectAllFromVoiceConfMsg.NAME, routing) - val body = EjectAllFromVoiceConfMsgBody(voiceConf) - val header = BbbCoreHeaderWithMeetingId(EjectAllFromVoiceConfMsg.NAME, meetingId) - val event = EjectAllFromVoiceConfMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - if (Users2x.numUsers(liveMeeting.users2x) == 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) - val event = buildEjectAllFromVoiceConfMsg(props.meetingProp.intId, props.voiceProp.voiceConf) - outGW.send(event) - } - } - } - - def monitorNumberOfUsers() { - val hasUsers = Users2x.numUsers(liveMeeting.users2x) != 0 - // TODO: We could use a better control over this message to send it just when it really matters :) - eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, UpdateMeetingExpireMonitor(props.meetingProp.intId, hasUsers))) - } - - def handleExtendMeetingDuration(msg: ExtendMeetingDuration) { - - } - - def startRecordingIfAutoStart() { - if (props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) && - props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) { - log.info("Auto start recording. meetingId={}", props.meetingProp.intId) - MeetingStatus2x.recordingStarted(liveMeeting.status) - - def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId) - val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing) - val body = RecordingStatusChangedEvtMsgBody(recording, userId) - val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId) - val event = RecordingStatusChangedEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - val event = buildRecordingStatusChangedEvtMsg( - liveMeeting.props.meetingProp.intId, - "system", MeetingStatus2x.isRecording(liveMeeting.status) - ) - outGW.send(event) - - } - } - - def stopAutoStartedRecording() { - if (props.recordProp.record && MeetingStatus2x.isRecording(liveMeeting.status) && - props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 0) { - log.info("Last web user left. Auto stopping recording. meetingId={}", props.meetingProp.intId) - MeetingStatus2x.recordingStopped(liveMeeting.status) - - def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId) - val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing) - val body = RecordingStatusChangedEvtMsgBody(recording, userId) - val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId) - val event = RecordingStatusChangedEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - val event = buildRecordingStatusChangedEvtMsg( - liveMeeting.props.meetingProp.intId, - "system", MeetingStatus2x.isRecording(liveMeeting.status) - ) - outGW.send(event) - - } - } - - def record(msg: BbbCoreMsg): Unit = { - if (liveMeeting.props.recordProp.record) { - outGW.record(msg) - } - } -} +package org.bigbluebutton.core.running + +import java.io.{ PrintWriter, StringWriter } + +import org.bigbluebutton.core.apps.users.UsersApp +import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingInactivityTracker, MeetingState2x } +import org.bigbluebutton.core.util.TimeUtil +//import java.util.concurrent.TimeUnit + +import akka.actor._ +import akka.actor.SupervisorStrategy.Resume +import org.bigbluebutton.common2.domain.DefaultProps +import org.bigbluebutton.core._ +import org.bigbluebutton.core.api._ +import org.bigbluebutton.core.apps._ +import org.bigbluebutton.core.apps.caption.CaptionApp2x +import org.bigbluebutton.core.apps.chat.ChatApp2x +import org.bigbluebutton.core.apps.screenshare.ScreenshareApp2x +import org.bigbluebutton.core.apps.presentation.PresentationApp2x +import org.bigbluebutton.core.apps.meeting._ +import org.bigbluebutton.core.apps.users.UsersApp2x +import org.bigbluebutton.core.apps.sharednotes.SharedNotesApp2x +import org.bigbluebutton.core.apps.whiteboard.WhiteboardApp2x +import org.bigbluebutton.core.bus._ +import org.bigbluebutton.core.models._ +import org.bigbluebutton.core2.MeetingStatus2x +import org.bigbluebutton.core2.message.handlers._ +import org.bigbluebutton.core2.message.handlers.users._ +import org.bigbluebutton.core2.message.handlers.meeting._ +import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.core.apps.breakout._ +import org.bigbluebutton.core.apps.polls._ +import org.bigbluebutton.core.apps.voice._ +import com.softwaremill.quicklens._ +import scala.concurrent.duration._ +import org.bigbluebutton.core2.testdata.FakeTestData +import org.bigbluebutton.core.apps.layout.LayoutApp2x +import org.bigbluebutton.core.apps.meeting.SyncGetMeetingInfoRespMsgHdlr + +object MeetingActor { + def props( + props: DefaultProps, + eventBus: IncomingEventBus, + outGW: OutMessageGateway, + liveMeeting: LiveMeeting + ): Props = + Props(classOf[MeetingActor], props, eventBus, outGW, liveMeeting) +} + +class MeetingActor( + val props: DefaultProps, + val eventBus: IncomingEventBus, + val outGW: OutMessageGateway, + val liveMeeting: LiveMeeting +) + extends BaseMeetingActor + with GuestsApp + with LayoutApp2x + with VoiceApp2x + with PollApp2x + with BreakoutApp2x + with UsersApp2x + with WhiteboardApp2x + + with PermisssionCheck + with UserBroadcastCamStartMsgHdlr + with UserJoinMeetingReqMsgHdlr + with UserBroadcastCamStopMsgHdlr + with UserConnectedToGlobalAudioMsgHdlr + with UserDisconnectedFromGlobalAudioMsgHdlr + with MuteAllExceptPresentersCmdMsgHdlr + with MuteMeetingCmdMsgHdlr + with IsMeetingMutedReqMsgHdlr + with MuteUserCmdMsgHdlr + with EjectUserFromVoiceCmdMsgHdlr + with EndMeetingSysCmdMsgHdlr + with DestroyMeetingSysCmdMsgHdlr + with SendTimeRemainingUpdateHdlr + with SyncGetMeetingInfoRespMsgHdlr { + + override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) { + case e: Exception => { + val sw: StringWriter = new StringWriter() + sw.write("An exception has been thrown on MeetingActor, exception message [" + e.getMessage() + "] (full stacktrace below)\n") + e.printStackTrace(new PrintWriter(sw)) + log.error(sw.toString()) + Resume + } + } + + /** + * Put the internal message injector into another actor so this + * actor is easy to test. + */ + var actorMonitor = context.actorOf( + MeetingActorAudit.props(props, eventBus, outGW), + "actorMonitor-" + props.meetingProp.intId + ) + + val presentationApp2x = new PresentationApp2x(liveMeeting, outGW) + val screenshareApp2x = new ScreenshareApp2x(liveMeeting, outGW) + val captionApp2x = new CaptionApp2x(liveMeeting, outGW) + val sharedNotesApp2x = new SharedNotesApp2x(liveMeeting, outGW) + val chatApp2x = new ChatApp2x(liveMeeting, outGW) + val usersApp = new UsersApp(liveMeeting, outGW, eventBus) + + val inactivityTrackerHelper = new MeetingInactivityTrackerHelper(liveMeeting, outGW) + val expiryTrackerHelper = new MeetingExpiryTrackerHelper(liveMeeting, outGW) + + val inactivityTracker = new MeetingInactivityTracker( + props.durationProps.maxInactivityTimeoutMinutes, + props.durationProps.warnMinutesBeforeMax, + lastActivityTimestamp = TimeUtil.timeNowInSeconds(), + warningSent = false, + warningSentOnTimestamp = 0L + ) + + val expiryTracker = new MeetingExpiryTracker( + startedOn = TimeUtil.timeNowInSeconds(), + userHasJoined = false, + lastUserLeftOn = 0L, + durationInMinutes = props.durationProps.duration, + meetingExpireIfNoUserJoinedInMinutes = props.durationProps.meetingExpireIfNoUserJoinedInMinutes, + meetingExpireWhenLastUserLeftInMinutes = props.durationProps.meetingExpireWhenLastUserLeftInMinutes + ) + + var state = new MeetingState2x(inactivityTracker, expiryTracker) + + /*******************************************************************/ + //object FakeTestData extends FakeTestData + //FakeTestData.createFakeUsers(liveMeeting) + /*******************************************************************/ + + def receive = { + //============================= + // 2x messages + 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) + + // Meeting + case m: DestroyMeetingSysCmdMsg => handleDestroyMeetingSysCmdMsg(m) + + //====================================== + + //======================================= + // old messages + case msg: MonitorNumberOfUsersInternalMsg => handleMonitorNumberOfUsers(msg) + + case msg: AllowUserToShareDesktop => handleAllowUserToShareDesktop(msg) + case msg: ExtendMeetingDuration => handleExtendMeetingDuration(msg) + case msg: SendTimeRemainingUpdate => state = handleSendTimeRemainingUpdate(msg, state) + + // Screenshare + case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg) + + case _ => // do nothing + } + + private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = { + // TODO: Update meeting activity status here + // updateActivityStatus(msg) + + msg.core match { + // Users + case m: ValidateAuthTokenReqMsg => state = usersApp.handleValidateAuthTokenReqMsg(m, state) + case m: UserJoinMeetingReqMsg => state = handleUserJoinMeetingReqMsg(m, state) + case m: UserLeaveReqMsg => state = handleUserLeaveReqMsg(m, state) + case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m) + case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m) + case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m) + case m: MeetingActivityResponseCmdMsg => + state = usersApp.handleMeetingActivityResponseCmdMsg(m, state, inactivityTrackerHelper) + case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m) + case m: SetRecordingStatusCmdMsg => usersApp.handleSetRecordingStatusCmdMsg(m) + case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m) + case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m) + case m: EjectUserFromMeetingCmdMsg => usersApp.handleEjectUserFromMeetingCmdMsg(m) + case m: GetUsersMeetingReqMsg => usersApp.handleGetUsersMeetingReqMsg(m) + + // Whiteboard + case m: SendCursorPositionPubMsg => handleSendCursorPositionPubMsg(m) + case m: ClearWhiteboardPubMsg => handleClearWhiteboardPubMsg(m) + case m: UndoWhiteboardPubMsg => handleUndoWhiteboardPubMsg(m) + case m: ModifyWhiteboardAccessPubMsg => handleModifyWhiteboardAccessPubMsg(m) + case m: GetWhiteboardAccessReqMsg => handleGetWhiteboardAccessReqMsg(m) + case m: SendWhiteboardAnnotationPubMsg => handleSendWhiteboardAnnotationPubMsg(m) + case m: GetWhiteboardAnnotationsReqMsg => handleGetWhiteboardAnnotationsReqMsg(m) + + // Poll + case m: StartPollReqMsg => handleStartPollReqMsg(m) + case m: StartCustomPollReqMsg => handleStartCustomPollReqMsg(m) + case m: StopPollReqMsg => handleStopPollReqMsg(m) + case m: ShowPollResultReqMsg => handleShowPollResultReqMsg(m) + case m: HidePollResultReqMsg => handleHidePollResultReqMsg(m) + case m: GetCurrentPollReqMsg => handleGetCurrentPollReqMsg(m) + case m: RespondToPollReqMsg => handleRespondToPollReqMsg(m) + + // Breakout + case m: BreakoutRoomsListMsg => handleBreakoutRoomsListMsg(m) + case m: CreateBreakoutRoomsCmdMsg => handleCreateBreakoutRoomsCmdMsg(m) + case m: EndAllBreakoutRoomsMsg => handleEndAllBreakoutRoomsMsg(m) + case m: RequestBreakoutJoinURLReqMsg => handleRequestBreakoutJoinURLReqMsg(m) + case m: BreakoutRoomCreatedMsg => handleBreakoutRoomCreatedMsg(m) + case m: BreakoutRoomEndedMsg => handleBreakoutRoomEndedMsg(m) + case m: BreakoutRoomUsersUpdateMsg => handleBreakoutRoomUsersUpdateMsg(m) + case m: SendBreakoutUsersUpdateMsg => handleSendBreakoutUsersUpdateMsg(m) + case m: TransferUserToMeetingRequestMsg => handleTransferUserToMeetingRequestMsg(m) + + // Voice + case m: UserLeftVoiceConfEvtMsg => handleUserLeftVoiceConfEvtMsg(m) + case m: UserMutedInVoiceConfEvtMsg => handleUserMutedInVoiceConfEvtMsg(m) + case m: UserTalkingInVoiceConfEvtMsg => handleUserTalkingInVoiceConfEvtMsg(m) + case m: RecordingStartedVoiceConfEvtMsg => handleRecordingStartedVoiceConfEvtMsg(m) + case m: MuteUserCmdMsg => handleMuteUserCmdMsg(m) + case m: MuteAllExceptPresentersCmdMsg => handleMuteAllExceptPresentersCmdMsg(m) + case m: EjectUserFromVoiceCmdMsg => handleEjectUserFromVoiceCmdMsg(m) + case m: IsMeetingMutedReqMsg => handleIsMeetingMutedReqMsg(m) + case m: MuteMeetingCmdMsg => handleMuteMeetingCmdMsg(m) + case m: UserConnectedToGlobalAudioMsg => handleUserConnectedToGlobalAudioMsg(m) + case m: UserDisconnectedFromGlobalAudioMsg => handleUserDisconnectedFromGlobalAudioMsg(m) + + // Layout + case m: GetCurrentLayoutReqMsg => handleGetCurrentLayoutReqMsg(m) + case m: LockLayoutMsg => handleLockLayoutMsg(m) + case m: BroadcastLayoutMsg => handleBroadcastLayoutMsg(m) + + // Presentation + case m: SetCurrentPresentationPubMsg => presentationApp2x.handleSetCurrentPresentationPubMsg(m) + case m: GetPresentationInfoReqMsg => presentationApp2x.handleGetPresentationInfoReqMsg(m) + case m: SetCurrentPagePubMsg => presentationApp2x.handleSetCurrentPagePubMsg(m) + case m: ResizeAndMovePagePubMsg => presentationApp2x.handleResizeAndMovePagePubMsg(m) + case m: RemovePresentationPubMsg => presentationApp2x.handleRemovePresentationPubMsg(m) + case m: PreuploadedPresentationsSysPubMsg => presentationApp2x.handlePreuploadedPresentationsPubMsg(m) + case m: PresentationConversionUpdateSysPubMsg => presentationApp2x.handlePresentationConversionUpdatePubMsg(m) + case m: PresentationPageCountErrorSysPubMsg => presentationApp2x.handlePresentationPageCountErrorPubMsg(m) + case m: PresentationPageGeneratedSysPubMsg => presentationApp2x.handlePresentationPageGeneratedPubMsg(m) + case m: PresentationConversionCompletedSysPubMsg => presentationApp2x.handlePresentationConversionCompletedPubMsg(m) + case m: AssignPresenterReqMsg => handlePresenterChange(m) + + // Caption + case m: EditCaptionHistoryPubMsg => captionApp2x.handleEditCaptionHistoryPubMsg(m) + case m: UpdateCaptionOwnerPubMsg => captionApp2x.handleUpdateCaptionOwnerPubMsg(m) + case m: SendCaptionHistoryReqMsg => captionApp2x.handleSendCaptionHistoryReqMsg(m) + + // SharedNotes + case m: GetSharedNotesPubMsg => sharedNotesApp2x.handleGetSharedNotesPubMsg(m) + case m: SyncSharedNotePubMsg => sharedNotesApp2x.handleSyncSharedNotePubMsg(m) + case m: UpdateSharedNoteReqMsg => sharedNotesApp2x.handleUpdateSharedNoteReqMsg(m) + case m: CreateSharedNoteReqMsg => sharedNotesApp2x.handleCreateSharedNoteReqMsg(m) + case m: DestroySharedNoteReqMsg => sharedNotesApp2x.handleDestroySharedNoteReqMsg(m) + + // Guests + case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m) + case m: SetGuestPolicyMsg => handleSetGuestPolicyMsg(m) + case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m) + + // Chat + case m: GetChatHistoryReqMsg => chatApp2x.handleGetChatHistoryReqMsg(m) + case m: SendPublicMessagePubMsg => chatApp2x.handleSendPublicMessagePubMsg(m) + case m: SendPrivateMessagePubMsg => chatApp2x.handleSendPrivateMessagePubMsg(m) + case m: ClearPublicChatHistoryPubMsg => chatApp2x.handleClearPublicChatHistoryPubMsg(m) + + // Screenshare + case m: ScreenshareStartedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareStartedVoiceConfEvtMsg(m) + case m: ScreenshareStoppedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareStoppedVoiceConfEvtMsg(m) + case m: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareRtmpBroadcastStartedVoiceConfEvtMsg(m) + case m: ScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg => screenshareApp2x.handleScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg(m) + + case _ => log.warning("***** Cannot handle " + msg.envelope.name) + } + } + + def handleGetAllMeetingsReqMsg(msg: GetAllMeetingsReqMsg): Unit = { + // sync all meetings + handleSyncGetMeetingInfoRespMsg(liveMeeting.props) + + // sync all users + usersApp.handleSyncGetUsersMeetingRespMsg() + + // sync all presentations + presentationApp2x.handleSyncGetPresentationInfoRespMsg() + + // TODO send all chat + // TODO send all lock settings + // TODO send all screen sharing info + } + + def handlePresenterChange(msg: AssignPresenterReqMsg): Unit = { + // Stop poll if one is running as presenter left + handleStopPollReqMsg(msg.header.userId) + + // switch user presenter status for old and new presenter + usersApp.handleAssignPresenterReqMsg(msg) + + // TODO stop current screen sharing session (initiated by the old presenter) + + } + + def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = { + + log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting=" + + ScreenshareModel.isBroadcastingRTMP(liveMeeting.screenshareModel) + " URL:" + + ScreenshareModel.getRTMPBroadcastingUrl(liveMeeting.screenshareModel)) + + if (ScreenshareModel.isBroadcastingRTMP(liveMeeting.screenshareModel)) { + // if the meeting has an ongoing WebRTC Deskshare session, send a notification + //outGW.send(new DeskShareNotifyASingleViewer(props.meetingProp.intId, msg.requesterID, + // DeskshareModel.getRTMPBroadcastingUrl(liveMeeting.deskshareModel), + // DeskshareModel.getDesktopShareVideoWidth(liveMeeting.deskshareModel), + // DeskshareModel.getDesktopShareVideoHeight(liveMeeting.deskshareModel), true)) + } + } + + def handleAllowUserToShareDesktop(msg: AllowUserToShareDesktop): Unit = { + Users2x.findPresenter(liveMeeting.users2x) match { + case Some(curPres) => { + val allowed = msg.userID equals (curPres.intId) + // outGW.send(AllowUserToShareDesktopOut(msg.meetingID, msg.userID, allowed)) + } + case None => // do nothing + } + } + + def handleMonitorNumberOfUsers(msg: MonitorNumberOfUsersInternalMsg) { + state = inactivityTrackerHelper.processMeetingInactivityAudit( + props = liveMeeting.props, + outGW, + eventBus, + state + ) + + state = expiryTrackerHelper.processMeetingExpiryAudit(liveMeeting.props, state, eventBus) + + monitorNumberOfWebUsers() + monitorNumberOfUsers() + } + + def monitorNumberOfWebUsers() { + + def buildEjectAllFromVoiceConfMsg(meetingId: String, voiceConf: String): BbbCommonEnvCoreMsg = { + val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") + val envelope = BbbCoreEnvelope(EjectAllFromVoiceConfMsg.NAME, routing) + val body = EjectAllFromVoiceConfMsgBody(voiceConf) + val header = BbbCoreHeaderWithMeetingId(EjectAllFromVoiceConfMsg.NAME, meetingId) + val event = EjectAllFromVoiceConfMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + if (Users2x.numUsers(liveMeeting.users2x) == 0 && + state.expiryTracker.lastUserLeftOn > 0) { + if (TimeUtil.timeNowInMinutes - state.expiryTracker.lastUserLeftOn > 2) { + log.info("Empty meeting. Ejecting all users from voice. meetingId={}", props.meetingProp.intId) + val event = buildEjectAllFromVoiceConfMsg(props.meetingProp.intId, props.voiceProp.voiceConf) + outGW.send(event) + } + } + } + + def monitorNumberOfUsers() { + val hasUsers = Users2x.numUsers(liveMeeting.users2x) != 0 + // TODO: We could use a better control over this message to send it just when it really matters :) + eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, UpdateMeetingExpireMonitor(props.meetingProp.intId, hasUsers))) + } + + def handleExtendMeetingDuration(msg: ExtendMeetingDuration) { + + } + + def startRecordingIfAutoStart() { + if (props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) && + props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) { + log.info("Auto start recording. meetingId={}", props.meetingProp.intId) + MeetingStatus2x.recordingStarted(liveMeeting.status) + + def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId) + val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing) + val body = RecordingStatusChangedEvtMsgBody(recording, userId) + val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId) + val event = RecordingStatusChangedEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + val event = buildRecordingStatusChangedEvtMsg( + liveMeeting.props.meetingProp.intId, + "system", MeetingStatus2x.isRecording(liveMeeting.status) + ) + outGW.send(event) + + } + } + + def stopAutoStartedRecording() { + if (props.recordProp.record && MeetingStatus2x.isRecording(liveMeeting.status) && + props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 0) { + log.info("Last web user left. Auto stopping recording. meetingId={}", props.meetingProp.intId) + MeetingStatus2x.recordingStopped(liveMeeting.status) + + def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId) + val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing) + val body = RecordingStatusChangedEvtMsgBody(recording, userId) + val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId) + val event = RecordingStatusChangedEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + val event = buildRecordingStatusChangedEvtMsg( + liveMeeting.props.meetingProp.intId, + "system", MeetingStatus2x.isRecording(liveMeeting.status) + ) + outGW.send(event) + + } + } + + def record(msg: BbbCoreMsg): Unit = { + if (liveMeeting.props.recordProp.record) { + outGW.record(msg) + } + } +} diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala index 022e3318ab..3aa361d986 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala @@ -4,8 +4,8 @@ import akka.actor.ActorContext import akka.event.Logging import org.bigbluebutton.common2.domain.DefaultProps import org.bigbluebutton.core.OutMessageGateway -import org.bigbluebutton.core.bus.{ IncomingEventBus } -import org.bigbluebutton.core.domain.MeetingExpiryTracker +import org.bigbluebutton.core.bus.IncomingEventBus +import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x } import org.bigbluebutton.core.util.TimeUtil class MeetingExpiryTrackerHelper( @@ -15,37 +15,29 @@ class MeetingExpiryTrackerHelper( val log = Logging(context.system, getClass) - def hasMeetingExpiredNeverBeenJoined(nowInMinutes: Long, startedOnInMinutes: Long, meetingExpireIfNoUserJoinedInMinutes: Long): Boolean = { - nowInMinutes - startedOnInMinutes > meetingExpireIfNoUserJoinedInMinutes - } - - def meetingOverDuration(nowInMinutes: Long, startedOnInMinutes: Long, durationInMinutes: Long): Boolean = { - nowInMinutes > startedOnInMinutes + durationInMinutes - } - - def processNeverBeenJoinedExpiry(nowInMinutes: Long, props: DefaultProps, tracker: MeetingExpiryTracker, eventBus: IncomingEventBus): MeetingExpiryTracker = { - if (hasMeetingExpiredNeverBeenJoined(nowInMinutes, tracker.startedOnInMinutes, - props.durationProps.meetingExpireIfNoUserJoinedInMinutes)) { + def processNeverBeenJoinedExpiry(nowInSeconds: Long, props: DefaultProps, state: MeetingState2x, + eventBus: IncomingEventBus): MeetingState2x = { + if (MeetingExpiryTracker.hasMeetingExpiredNeverBeenJoined(state, nowInSeconds)) { log.info("Ending meeting as it has never been joined.") sendEndMeetingDueToExpiry(props, eventBus) - tracker + state } else { - tracker + state } } - def processMeetingExpiryAudit(props: DefaultProps, tracker: MeetingExpiryTracker, eventBus: IncomingEventBus): MeetingExpiryTracker = { - val nowInMinutes = TimeUtil.millisToMinutes(System.currentTimeMillis()) + def processMeetingExpiryAudit(props: DefaultProps, state: MeetingState2x, eventBus: IncomingEventBus): MeetingState2x = { + val nowInSeconds = TimeUtil.timeNowInSeconds() - if (!tracker.meetingJoined) { - processNeverBeenJoinedExpiry(nowInMinutes, props, tracker, eventBus) + if (!state.expiryTracker.userHasJoined) { + processNeverBeenJoinedExpiry(nowInSeconds, props, state, eventBus) } else { - if (props.durationProps.duration != 0 && meetingOverDuration(nowInMinutes, tracker.startedOnInMinutes, props.durationProps.duration)) { + if (props.durationProps.duration != 0 && MeetingExpiryTracker.meetingOverDuration(state, nowInSeconds)) { log.info("Ending meeting as it has passed duration.") sendEndMeetingDueToExpiry(props, eventBus) - tracker + state } else { - tracker + state } } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingInactivityTrackerHelper.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingInactivityTrackerHelper.scala index 0c929fb25d..eaa592636a 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingInactivityTrackerHelper.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingInactivityTrackerHelper.scala @@ -1,128 +1,100 @@ -package org.bigbluebutton.core.running - -import akka.actor.ActorContext -import akka.event.Logging -import org.bigbluebutton.core.domain.MeetingInactivityTracker -import com.softwaremill.quicklens._ -import org.bigbluebutton.common2.domain.DefaultProps -import org.bigbluebutton.common2.msgs._ -import org.bigbluebutton.core.OutMessageGateway -import org.bigbluebutton.core.bus.{ IncomingEventBus } -import org.bigbluebutton.core.util.TimeUtil - -class MeetingInactivityTrackerHelper( - val liveMeeting: LiveMeeting, - val outGW: OutMessageGateway -)(implicit val context: ActorContext) extends HandlerHelpers { - - val log = Logging(context.system, getClass) - - def isMeetingActive( - nowInMinutes: Long, - lastActivityTimeInMinutes: Long, - maxInactivityTimeoutMinutes: Long - ): Boolean = { - nowInMinutes - lastActivityTimeInMinutes < maxInactivityTimeoutMinutes - } - - def isMeetingInactive( - warningSent: Boolean, - nowInMinutes: Long, - lastActivityTimeInMinutes: Long, - maxInactivityTimeoutMinutes: Long - ): Boolean = { - warningSent && (nowInMinutes - lastActivityTimeInMinutes) > maxInactivityTimeoutMinutes - } - - def processMeetingInactivityAudit( - props: DefaultProps, - outGW: OutMessageGateway, - eventBus: IncomingEventBus, - tracker: MeetingInactivityTracker - ): MeetingInactivityTracker = { - - val nowInMinutes = TimeUtil.millisToMinutes(System.currentTimeMillis()) - if (isMeetingActive(nowInMinutes, tracker.lastActivityTimeInMinutes, tracker.maxInactivityTimeoutMinutes)) { - tracker - } else { - if (isMeetingInactive(tracker.warningSent, nowInMinutes, - tracker.lastActivityTimeInMinutes, - tracker.maxInactivityTimeoutMinutes)) { - sendEndMeetingDueToInactivity(props, eventBus) - tracker - } else { - if (tracker.warningSent) { - tracker - } else { - warnOfMeetingInactivity(props, outGW, nowInMinutes, tracker) - tracker.modify(_.warningSent).setTo(true).modify(_.warningSentOnTimeInMinutes).setTo(nowInMinutes) - } - } - } - } - - def timeLeftInMinutes(nowInMinutes: Long, tracker: MeetingInactivityTracker): Long = { - tracker.lastActivityTimeInMinutes + tracker.maxInactivityTimeoutMinutes - nowInMinutes - } - - def warnOfMeetingInactivity(props: DefaultProps, outGW: OutMessageGateway, - nowInMinutes: Long, tracker: MeetingInactivityTracker): MeetingInactivityTracker = { - val timeLeftSeconds = TimeUtil.minutesToSeconds(timeLeftInMinutes(nowInMinutes, tracker)) - sendMeetingInactivityWarning(props, outGW, timeLeftSeconds) - tracker - } - - def sendEndMeetingDueToInactivity(props: DefaultProps, eventBus: IncomingEventBus): Unit = { - endMeeting(outGW, liveMeeting) - - if (liveMeeting.props.meetingProp.isBreakout) { - log.info( - "Informing parent meeting {} that a breakout room has been ended {}", - liveMeeting.props.breakoutProps.parentId, liveMeeting.props.meetingProp.intId - ) - notifyParentThatBreakoutEnded(eventBus, liveMeeting) - } - - destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId) - } - - def sendMeetingInactivityWarning(props: DefaultProps, outGW: OutMessageGateway, timeLeftSeconds: Long): Unit = { - def build(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") - val envelope = BbbCoreEnvelope(MeetingInactivityWarningEvtMsg.NAME, routing) - val body = MeetingInactivityWarningEvtMsgBody(timeLeftInSec) - val header = BbbClientMsgHeader(MeetingInactivityWarningEvtMsg.NAME, meetingId, "not-used") - val event = MeetingInactivityWarningEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - val event = build(props.meetingProp.intId, timeLeftSeconds) - outGW.send(event) - } - - def processMeetingActivityResponse( - props: DefaultProps, - outGW: OutMessageGateway, - msg: MeetingActivityResponseCmdMsg, - tracker: MeetingInactivityTracker - ): MeetingInactivityTracker = { - - def buildMeetingIsActiveEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") - val envelope = BbbCoreEnvelope(MeetingIsActiveEvtMsg.NAME, routing) - val body = MeetingIsActiveEvtMsgBody(meetingId) - val header = BbbClientMsgHeader(MeetingIsActiveEvtMsg.NAME, meetingId, "not-used") - val event = MeetingIsActiveEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - val event = buildMeetingIsActiveEvtMsg(props.meetingProp.intId) - outGW.send(event) - - tracker.modify(_.warningSent).setTo(false) - .modify(_.warningSentOnTimeInMinutes).setTo(0L) - .modify(_.lastActivityTimeInMinutes).setTo(System.currentTimeMillis()) - } -} +package org.bigbluebutton.core.running + +import akka.actor.ActorContext +import akka.event.Logging +import org.bigbluebutton.core.domain.{ MeetingInactivityTracker, MeetingState2x } +import com.softwaremill.quicklens._ +import org.bigbluebutton.common2.domain.DefaultProps +import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.core.OutMessageGateway +import org.bigbluebutton.core.bus.IncomingEventBus +import org.bigbluebutton.core.util.TimeUtil + +class MeetingInactivityTrackerHelper( + val liveMeeting: LiveMeeting, + val outGW: OutMessageGateway +)(implicit val context: ActorContext) extends HandlerHelpers { + + val log = Logging(context.system, getClass) + + def processMeetingInactivityAudit( + props: DefaultProps, + outGW: OutMessageGateway, + eventBus: IncomingEventBus, + state: MeetingState2x + ): MeetingState2x = { + + val nowInSeconds = TimeUtil.timeNowInSeconds() + if (!MeetingInactivityTracker.hasRecentActivity(state, nowInSeconds)) { + if (MeetingInactivityTracker.isMeetingInactive(state, nowInSeconds)) { + sendEndMeetingDueToInactivity(props, eventBus) + state + } else { + if (!MeetingInactivityTracker.warningHasBeenSent(state)) { + warnOfMeetingInactivity(props, outGW, nowInSeconds, state) + MeetingInactivityTracker.setWarningSentAndTimestamp(state, nowInSeconds) + } else { + state + } + } + } else { + state + } + } + + def warnOfMeetingInactivity(props: DefaultProps, outGW: OutMessageGateway, + nowInSeconds: Long, state: MeetingState2x): Unit = { + val timeLeftSeconds = MeetingInactivityTracker.timeLeftInSeconds(state, nowInSeconds) + sendMeetingInactivityWarning(props, outGW, timeLeftSeconds) + } + + def sendEndMeetingDueToInactivity(props: DefaultProps, eventBus: IncomingEventBus): Unit = { + endMeeting(outGW, liveMeeting) + + if (liveMeeting.props.meetingProp.isBreakout) { + log.info( + "Informing parent meeting {} that a breakout room has been ended {}", + liveMeeting.props.breakoutProps.parentId, liveMeeting.props.meetingProp.intId + ) + notifyParentThatBreakoutEnded(eventBus, liveMeeting) + } + + destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId) + } + + def sendMeetingInactivityWarning(props: DefaultProps, outGW: OutMessageGateway, timeLeftSeconds: Long): Unit = { + def build(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") + val envelope = BbbCoreEnvelope(MeetingInactivityWarningEvtMsg.NAME, routing) + val body = MeetingInactivityWarningEvtMsgBody(timeLeftInSec) + val header = BbbClientMsgHeader(MeetingInactivityWarningEvtMsg.NAME, meetingId, "not-used") + val event = MeetingInactivityWarningEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + val event = build(props.meetingProp.intId, timeLeftSeconds) + outGW.send(event) + } + + def processMeetingActivityResponse( + props: DefaultProps, + outGW: OutMessageGateway, + msg: MeetingActivityResponseCmdMsg + ): Unit = { + + def buildMeetingIsActiveEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") + val envelope = BbbCoreEnvelope(MeetingIsActiveEvtMsg.NAME, routing) + val body = MeetingIsActiveEvtMsgBody(meetingId) + val header = BbbClientMsgHeader(MeetingIsActiveEvtMsg.NAME, meetingId, "not-used") + val event = MeetingIsActiveEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + val event = buildMeetingIsActiveEvtMsg(props.meetingProp.intId) + outGW.send(event) + + } +} diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/RunningMeeting.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/RunningMeeting.scala index 45e097118a..698f64c9b2 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/RunningMeeting.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/RunningMeeting.scala @@ -1,7 +1,7 @@ package org.bigbluebutton.core.running import akka.actor.ActorContext -import org.bigbluebutton.common2.domain.{ DefaultProps } +import org.bigbluebutton.common2.domain.DefaultProps import org.bigbluebutton.core.apps._ import org.bigbluebutton.core.bus._ import org.bigbluebutton.core.models._ diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/util/TimeUtil.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/util/TimeUtil.scala index 67bd7c95b1..74dfc7688f 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/util/TimeUtil.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/util/TimeUtil.scala @@ -3,6 +3,7 @@ package org.bigbluebutton.core.util import java.util.concurrent.TimeUnit object TimeUtil { + def minutesToMillis(minutes: Long): Long = { TimeUnit.MINUTES.toMillis(minutes) } @@ -14,4 +15,7 @@ object TimeUtil { def minutesToSeconds(minutes: Long): Long = { TimeUnit.MINUTES.toSeconds(minutes) } + + def timeNowInMinutes(): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime()) + def timeNowInSeconds(): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime()) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/MeetingStatus2x.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/MeetingStatus2x.scala index dc3842d2bd..1bd918e1ca 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/MeetingStatus2x.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/MeetingStatus2x.scala @@ -39,9 +39,6 @@ object MeetingStatus2x { 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 @@ -57,10 +54,6 @@ object MeetingStatus2x { def timeNowInMinutes(status: MeetingStatus2x): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime()) def timeNowInSeconds(status: MeetingStatus2x): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime()) - def startedOn(status: MeetingStatus2x): Long = status.startedOn - - def timeNowInMinutes(): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime()) - def timeNowInSeconds(): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime()) } class MeetingStatus2x { @@ -75,15 +68,10 @@ class MeetingStatus2x { private var meetingEnded = false private var meetingMuted = false - private var hasLastWebUserLeft = false - private var lastWebUserLeftOnTimestamp: Long = 0 - private var voiceRecordingFilename: String = "" private var extension = new MeetingExtensionProp - private val startedOn = MeetingStatus2x.timeNowInSeconds; - private def startRecordingVoice() { recordingVoice = true } @@ -115,9 +103,7 @@ class MeetingStatus2x { 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 diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/SendTimeRemainingUpdateHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/SendTimeRemainingUpdateHdlr.scala index ba78103231..ae5501e70f 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/SendTimeRemainingUpdateHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/SendTimeRemainingUpdateHdlr.scala @@ -2,10 +2,11 @@ package org.bigbluebutton.core2.message.handlers import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway -import org.bigbluebutton.core.api.{ SendTimeRemainingUpdate } +import org.bigbluebutton.core.api.SendTimeRemainingUpdate +import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x } import org.bigbluebutton.core.models.BreakoutRooms import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting } -import org.bigbluebutton.core2.MeetingStatus2x +import org.bigbluebutton.core.util.TimeUtil trait SendTimeRemainingUpdateHdlr { this: BaseMeetingActor => @@ -13,10 +14,11 @@ trait SendTimeRemainingUpdateHdlr { val liveMeeting: LiveMeeting val outGW: OutMessageGateway - def handleSendTimeRemainingUpdate(msg: SendTimeRemainingUpdate) { + def handleSendTimeRemainingUpdate(msg: SendTimeRemainingUpdate, state: MeetingState2x): MeetingState2x = { + if (liveMeeting.props.durationProps.duration > 0) { - val endMeetingTime = MeetingStatus2x.startedOn(liveMeeting.status) + (liveMeeting.props.durationProps.duration * 60) - val timeRemaining = endMeetingTime - liveMeeting.timeNowInSeconds + val endMeetingTime = MeetingExpiryTracker.endMeetingTime(state) + val timeRemaining = endMeetingTime - TimeUtil.timeNowInSeconds def buildMeetingTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = { val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") @@ -34,7 +36,7 @@ trait SendTimeRemainingUpdateHdlr { if (!liveMeeting.props.meetingProp.isBreakout && !BreakoutRooms.getRooms(liveMeeting.breakoutRooms).isEmpty) { val endMeetingTime = BreakoutRooms.breakoutRoomsStartedOn(liveMeeting.breakoutRooms) + (BreakoutRooms.breakoutRoomsdurationInMinutes(liveMeeting.breakoutRooms) * 60) - val timeRemaining = endMeetingTime - liveMeeting.timeNowInSeconds + val timeRemaining = endMeetingTime - TimeUtil.timeNowInSeconds def buildBreakoutRoomsTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = { val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") @@ -53,6 +55,8 @@ trait SendTimeRemainingUpdateHdlr { BreakoutRooms.breakoutRoomsdurationInMinutes(liveMeeting.breakoutRooms, 0) BreakoutRooms.breakoutRoomsStartedOn(liveMeeting.breakoutRooms, 0) } + + state } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserJoinMeetingReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserJoinMeetingReqMsgHdlr.scala index f2ade60ac9..fc08667275 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserJoinMeetingReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserJoinMeetingReqMsgHdlr.scala @@ -1,8 +1,9 @@ package org.bigbluebutton.core2.message.handlers.users import org.bigbluebutton.common2.msgs.UserJoinMeetingReqMsg -import org.bigbluebutton.core.{ OutMessageGateway } -import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting, BaseMeetingActor } +import org.bigbluebutton.core.OutMessageGateway +import org.bigbluebutton.core.domain.MeetingState2x +import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting } trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers { this: BaseMeetingActor => @@ -10,8 +11,8 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers { val liveMeeting: LiveMeeting val outGW: OutMessageGateway - def handleUserJoinMeetingReqMsg(msg: UserJoinMeetingReqMsg): Unit = { - userJoinMeeting(outGW, msg.body.authToken, liveMeeting) + def handleUserJoinMeetingReqMsg(msg: UserJoinMeetingReqMsg, state: MeetingState2x): MeetingState2x = { + userJoinMeeting(outGW, msg.body.authToken, liveMeeting, state) } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala index d3211d11ac..4e1516fd7f 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala @@ -3,6 +3,7 @@ package org.bigbluebutton.core2.message.handlers.users import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.apps.users.UsersApp +import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.models._ import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting } @@ -12,29 +13,32 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers { val liveMeeting: LiveMeeting val outGW: OutMessageGateway - def handleValidateAuthTokenReqMsg(msg: ValidateAuthTokenReqMsg): Unit = { + def handleValidateAuthTokenReqMsg(msg: ValidateAuthTokenReqMsg, state: MeetingState2x): MeetingState2x = { log.debug("RECEIVED ValidateAuthTokenReqMsg msg {}", msg) RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers) match { case Some(u) => val guestPolicyType = GuestsWaiting.getGuestPolicy(liveMeeting.guestsWaiting).policy if (guestPolicyType == GuestPolicyType.ALWAYS_ACCEPT) { - userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u) + userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u, state) } else if (guestPolicyType == GuestPolicyType.ASK_MODERATOR) { if (u.guest && u.waitingForAcceptance) { userValidatedButNeedToWaitForApproval(outGW, liveMeeting, u) + state } else { - userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u) + userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u, state) } } else { validateTokenFailed(outGW, meetingId = liveMeeting.props.meetingProp.intId, userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false) // TODO: Disconnect user + state } case None => validateTokenFailed(outGW, meetingId = liveMeeting.props.meetingProp.intId, userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false) - // TODO: Disconnect user + // TODO: Disconnect user + state } } } From e62efc3820d94f192a5e5ab8dc8aff6a1d2492b4 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Mon, 17 Jul 2017 14:59:31 -0700 Subject: [PATCH 03/10] - try to figure out why meeting doesn't end when user leaves --- .../domain/MeetingInactivityTracker.scala | 8 ++++---- .../core/running/HandlerHelpers.scala | 20 +++++++++---------- .../users/ValidateAuthTokenReqMsgHdlr.scala | 11 +++------- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala index 93de876681..d0d59f0e2d 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala @@ -58,13 +58,13 @@ object MeetingExpiryTracker { } def setUserHasJoined(state: MeetingState2x): MeetingState2x = { - state.expiryTracker.modify(_.userHasJoined).setTo(true) - state + val tracker = state.expiryTracker.modify(_.userHasJoined).setTo(true) + state.modify(_.expiryTracker).setTo(tracker) } def setLastUserLeftOn(state: MeetingState2x, timestampInSeconds: Long): MeetingState2x = { - state.expiryTracker.modify(_.lastUserLeftOn).setTo(timestampInSeconds) - state + val tracker = state.expiryTracker.modify(_.lastUserLeftOn).setTo(timestampInSeconds) + state.modify(_.expiryTracker).setTo(tracker) } def hasMeetingExpiredNeverBeenJoined(state: MeetingState2x, nowInSeconds: Long): Boolean = { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala index 9534b2d436..3e43a45ce9 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala @@ -5,7 +5,7 @@ import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.api.{ BreakoutRoomEndedInternalMsg, DestroyMeetingInternalMsg, RecordingStatusChanged } import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus } import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x } -import org.bigbluebutton.core.{ MessageRecorder, OutMessageGateway } +import org.bigbluebutton.core.{ OutMessageGateway } import org.bigbluebutton.core.models._ import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender, UserJoinedMeetingEvtMsgBuilder } @@ -13,33 +13,31 @@ import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender, UserJoinedM trait HandlerHelpers extends SystemConfiguration { def validateTokenFailed(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String, - valid: Boolean, waitForApproval: Boolean): Unit = { - val event = MsgBuilder.buildValidateAuthTokenRespMsg( - meetingId, - userId, authToken, valid, waitForApproval - ) + valid: Boolean, waitForApproval: Boolean, state: MeetingState2x): MeetingState2x = { + val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval) Sender.send(outGW, event) // TODO: Should disconnect user here. + + state } def sendValidateAuthTokenRespMsg(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String, valid: Boolean, waitForApproval: Boolean): Unit = { - val event = MsgBuilder.buildValidateAuthTokenRespMsg( - meetingId, - userId, authToken, valid, waitForApproval - ) + val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval) Sender.send(outGW, event) } def userValidatedButNeedToWaitForApproval(outGW: OutMessageGateway, liveMeeting: LiveMeeting, - user: RegisteredUser): Unit = { + user: RegisteredUser, state: MeetingState2x): MeetingState2x = { val meetingId = liveMeeting.props.meetingProp.intId sendValidateAuthTokenRespMsg(outGW, meetingId, user.id, user.authToken, valid = true, waitForApproval = false) val guest = GuestWaiting(user.id, user.name, user.role) addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting) notifyModeratorsOfGuestWaiting(outGW, Vector(guest), liveMeeting.users2x, meetingId) + + state } def addGuestToWaitingForApproval(guest: GuestWaiting, guestsWaitingList: GuestsWaiting): Unit = { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala index 4e1516fd7f..cae96d5cd4 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala @@ -23,22 +23,17 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers { userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u, state) } else if (guestPolicyType == GuestPolicyType.ASK_MODERATOR) { if (u.guest && u.waitingForAcceptance) { - userValidatedButNeedToWaitForApproval(outGW, liveMeeting, u) - state + userValidatedButNeedToWaitForApproval(outGW, liveMeeting, u, state) } else { userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u, state) } } else { validateTokenFailed(outGW, meetingId = liveMeeting.props.meetingProp.intId, - userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false) - // TODO: Disconnect user - state + userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false, state) } case None => validateTokenFailed(outGW, meetingId = liveMeeting.props.meetingProp.intId, - userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false) - // TODO: Disconnect user - state + userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false, state) } } } From a61030a95f788133d4feb9d3836ab37484fd20ae Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Tue, 18 Jul 2017 14:47:42 -0700 Subject: [PATCH 04/10] - track meeting inactivity and expiry in akka-apps --- .../users/LogoutAndEndMeetingCmdMsgHdlr.scala | 15 +-- .../MeetingActivityResponseCmdMsgHdlr.scala | 31 +++++- .../domain/MeetingInactivityTracker.scala | 57 ++++++---- .../core/domain/MeetingState2x.scala | 8 ++ .../core/running/HandlerHelpers.scala | 30 ++++-- .../core/running/MeetingActor.scala | 56 ++-------- .../running/MeetingExpiryTrackerHelper.scala | 76 ++++++++----- .../MeetingInactivityTrackerHelper.scala | 100 ------------------ .../meeting/EndMeetingSysCmdMsgHdlr.scala | 13 +-- .../users/ValidateAuthTokenReqMsgHdlr.scala | 1 + .../core2/message/senders/MsgBuilder.scala | 20 ---- .../bigbluebutton/client/MsgToClientGW.scala | 2 - .../common2/msgs/SystemMsgs.scala | 2 +- .../messaging/ConnectionInvokerService.java | 4 +- 14 files changed, 162 insertions(+), 253 deletions(-) delete mode 100755 akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingInactivityTrackerHelper.scala diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/LogoutAndEndMeetingCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/LogoutAndEndMeetingCmdMsgHdlr.scala index 90a21bead6..c13562218f 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/LogoutAndEndMeetingCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/LogoutAndEndMeetingCmdMsgHdlr.scala @@ -2,7 +2,8 @@ package org.bigbluebutton.core.apps.users import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway -import org.bigbluebutton.core.bus.{ IncomingEventBus } +import org.bigbluebutton.core.bus.IncomingEventBus +import org.bigbluebutton.core.domain.MeetingEndReason import org.bigbluebutton.core.models.{ Roles, Users2x } import org.bigbluebutton.core.running.LiveMeeting @@ -18,17 +19,7 @@ trait LogoutAndEndMeetingCmdMsgHdlr { u <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) } yield { if (u.role == Roles.MODERATOR_ROLE) { - endMeeting(outGW, liveMeeting) - - if (liveMeeting.props.meetingProp.isBreakout) { - log.info( - "Informing parent meeting {} that a breakout room has been ended {}", - liveMeeting.props.breakoutProps.parentId, liveMeeting.props.meetingProp.intId - ) - notifyParentThatBreakoutEnded(eventBus, liveMeeting) - } - - destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId) + sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_AFTER_USER_LOGGED_OUT, eventBus, outGW, liveMeeting) } } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MeetingActivityResponseCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MeetingActivityResponseCmdMsgHdlr.scala index a6f52251ce..ac22a7d39d 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MeetingActivityResponseCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MeetingActivityResponseCmdMsgHdlr.scala @@ -1,9 +1,10 @@ package org.bigbluebutton.core.apps.users +import org.bigbluebutton.common2.domain.DefaultProps import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.domain.{ MeetingInactivityTracker, MeetingState2x } -import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting, MeetingInactivityTrackerHelper } +import org.bigbluebutton.core.running.{ LiveMeeting } trait MeetingActivityResponseCmdMsgHdlr { this: UsersApp => @@ -12,11 +13,31 @@ trait MeetingActivityResponseCmdMsgHdlr { val outGW: OutMessageGateway def handleMeetingActivityResponseCmdMsg( - msg: MeetingActivityResponseCmdMsg, - state: MeetingState2x, - helper: MeetingInactivityTrackerHelper + msg: MeetingActivityResponseCmdMsg, + state: MeetingState2x ): MeetingState2x = { - helper.processMeetingActivityResponse(liveMeeting.props, outGW, msg) + processMeetingActivityResponse(liveMeeting.props, outGW, msg) MeetingInactivityTracker.resetWarningSentAndTimestamp(state) } + + def processMeetingActivityResponse( + props: DefaultProps, + outGW: OutMessageGateway, + msg: MeetingActivityResponseCmdMsg + ): Unit = { + + def buildMeetingIsActiveEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") + val envelope = BbbCoreEnvelope(MeetingIsActiveEvtMsg.NAME, routing) + val body = MeetingIsActiveEvtMsgBody(meetingId) + val header = BbbClientMsgHeader(MeetingIsActiveEvtMsg.NAME, meetingId, "not-used") + val event = MeetingIsActiveEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + val event = buildMeetingIsActiveEvtMsg(props.meetingProp.intId) + outGW.send(event) + + } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala index d0d59f0e2d..248ba33c02 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala @@ -3,6 +3,14 @@ package org.bigbluebutton.core.domain import com.softwaremill.quicklens._ import org.bigbluebutton.core.util.TimeUtil +case class MeetingInactivityTracker( + val maxInactivityTimeoutMinutes: Int, + val warningMinutesBeforeMax: Int, + lastActivityTimestamp: Long, + warningSent: Boolean, + warningSentOnTimestamp: Long +) + object MeetingInactivityTracker { def warningHasBeenSent(state: MeetingState2x): Boolean = { @@ -28,7 +36,8 @@ object MeetingInactivityTracker { def hasRecentActivity(state: MeetingState2x, nowInSeconds: Long): Boolean = { nowInSeconds - state.inactivityTracker.lastActivityTimestamp < - TimeUtil.minutesToSeconds(state.inactivityTracker.maxInactivityTimeoutMinutes) + TimeUtil.minutesToSeconds(state.inactivityTracker.maxInactivityTimeoutMinutes) - + TimeUtil.minutesToSeconds(state.inactivityTracker.warningMinutesBeforeMax) } def isMeetingInactive(state: MeetingState2x, nowInSeconds: Long): Boolean = { @@ -43,12 +52,13 @@ object MeetingInactivityTracker { } } -case class MeetingInactivityTracker( - val maxInactivityTimeoutMinutes: Int, - val warningMinutesBeforeMax: Int, - lastActivityTimestamp: Long, - warningSent: Boolean, - warningSentOnTimestamp: Long +case class MeetingExpiryTracker( + startedOn: Long, + userHasJoined: Boolean, + lastUserLeftOn: Option[Long], + durationInMinutes: Int, + meetingExpireIfNoUserJoinedInMinutes: Int, + meetingExpireWhenLastUserLeftInMinutes: Int ) object MeetingExpiryTracker { @@ -59,21 +69,38 @@ object MeetingExpiryTracker { def setUserHasJoined(state: MeetingState2x): MeetingState2x = { val tracker = state.expiryTracker.modify(_.userHasJoined).setTo(true) + .modify(_.lastUserLeftOn).setTo(None) state.modify(_.expiryTracker).setTo(tracker) } + def hasMeetingExpiredAfterLastUserLeft(state: MeetingState2x, timestampInSeconds: Long): Boolean = { + val expire = for { + lastUserLeftOn <- state.expiryTracker.lastUserLeftOn + } yield { + timestampInSeconds - lastUserLeftOn > + TimeUtil.minutesToSeconds(state.expiryTracker.meetingExpireWhenLastUserLeftInMinutes) + } + + expire.getOrElse(false) + } + def setLastUserLeftOn(state: MeetingState2x, timestampInSeconds: Long): MeetingState2x = { - val tracker = state.expiryTracker.modify(_.lastUserLeftOn).setTo(timestampInSeconds) + val tracker = state.expiryTracker.modify(_.lastUserLeftOn).setTo(Some(timestampInSeconds)) state.modify(_.expiryTracker).setTo(tracker) } def hasMeetingExpiredNeverBeenJoined(state: MeetingState2x, nowInSeconds: Long): Boolean = { - nowInSeconds - state.expiryTracker.startedOn > - TimeUtil.minutesToSeconds(state.expiryTracker.meetingExpireIfNoUserJoinedInMinutes) + !state.expiryTracker.userHasJoined && + (nowInSeconds - state.expiryTracker.startedOn > + TimeUtil.minutesToSeconds(state.expiryTracker.meetingExpireIfNoUserJoinedInMinutes)) } def meetingOverDuration(state: MeetingState2x, nowInSeconds: Long): Boolean = { - nowInSeconds > state.expiryTracker.startedOn + TimeUtil.minutesToSeconds(state.expiryTracker.durationInMinutes) + if (state.expiryTracker.durationInMinutes == 0) { + false + } else { + nowInSeconds > state.expiryTracker.startedOn + TimeUtil.minutesToSeconds(state.expiryTracker.durationInMinutes) + } } def endMeetingTime(state: MeetingState2x): Int = { @@ -81,11 +108,3 @@ object MeetingExpiryTracker { } } -case class MeetingExpiryTracker( - startedOn: Long, - userHasJoined: Boolean, - lastUserLeftOn: Long, - durationInMinutes: Int, - meetingExpireIfNoUserJoinedInMinutes: Int, - meetingExpireWhenLastUserLeftInMinutes: Int -) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingState2x.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingState2x.scala index f86cedfdd7..bb880efc73 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingState2x.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingState2x.scala @@ -9,3 +9,11 @@ case class MeetingState2x( expiryTracker: MeetingExpiryTracker ) +object MeetingEndReason { + val ENDED_FROM_API = "ENDED_FROM_API" + val ENDED_DUE_TO_INACTIVITY = "ENDED_DUE_TO_ACTIVITY" + val ENDED_WHEN_NOT_JOINED = "ENDED_WHEN_NOT_JOINED" + val ENDED_WHEN_LAST_USER_LEFT = "ENDED_WHEN_LAST_USER_LEFT" + val ENDED_AFTER_USER_LOGGED_OUT = "ENDED_AFTER_USER_LOGGED_OUT" + val ENDED_AFTER_EXCEEDING_DURATION = "ENDED_AFTER_USER_LOGGED_OUT" +} \ No newline at end of file diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala index 3e43a45ce9..746ac79428 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala @@ -1,12 +1,14 @@ package org.bigbluebutton.core.running import org.bigbluebutton.SystemConfiguration +import org.bigbluebutton.common2.domain.DefaultProps import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.api.{ BreakoutRoomEndedInternalMsg, DestroyMeetingInternalMsg, RecordingStatusChanged } import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus } import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x } -import org.bigbluebutton.core.{ OutMessageGateway } +import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.models._ +import org.bigbluebutton.core.util.TimeUtil import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender, UserJoinedMeetingEvtMsgBuilder } @@ -178,11 +180,11 @@ trait HandlerHelpers extends SystemConfiguration { outGW.send(event) } - def endMeeting(outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = { + def endMeeting(outGW: OutMessageGateway, liveMeeting: LiveMeeting, reason: String): Unit = { def buildMeetingEndingEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") val envelope = BbbCoreEnvelope(MeetingEndingEvtMsg.NAME, routing) - val body = MeetingEndingEvtMsgBody(meetingId) + val body = MeetingEndingEvtMsgBody(meetingId, reason) val header = BbbClientMsgHeader(MeetingEndingEvtMsg.NAME, meetingId, "not-used") val event = MeetingEndingEvtMsg(header, body) @@ -215,9 +217,23 @@ trait HandlerHelpers extends SystemConfiguration { } def notifyParentThatBreakoutEnded(eventBus: IncomingEventBus, liveMeeting: LiveMeeting): Unit = { - eventBus.publish(BigBlueButtonEvent( - liveMeeting.props.breakoutProps.parentId, - new BreakoutRoomEndedInternalMsg(liveMeeting.props.meetingProp.intId) - )) + if (liveMeeting.props.meetingProp.isBreakout) { + eventBus.publish(BigBlueButtonEvent( + liveMeeting.props.breakoutProps.parentId, + new BreakoutRoomEndedInternalMsg(liveMeeting.props.meetingProp.intId) + )) + } + } + + def ejectAllUsersFromVoiceConf(outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = { + val event = MsgBuilder.buildEjectAllFromVoiceConfMsg(liveMeeting.props.meetingProp.intId, liveMeeting.props.voiceProp.voiceConf) + outGW.send(event) + } + + def sendEndMeetingDueToExpiry(reason: String, eventBus: IncomingEventBus, outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = { + endMeeting(outGW, liveMeeting, reason) + notifyParentThatBreakoutEnded(eventBus, liveMeeting) + ejectAllUsersFromVoiceConf(outGW, liveMeeting) + destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId) } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala index c83dc77699..3490373742 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala @@ -104,7 +104,6 @@ class MeetingActor( val chatApp2x = new ChatApp2x(liveMeeting, outGW) val usersApp = new UsersApp(liveMeeting, outGW, eventBus) - val inactivityTrackerHelper = new MeetingInactivityTrackerHelper(liveMeeting, outGW) val expiryTrackerHelper = new MeetingExpiryTrackerHelper(liveMeeting, outGW) val inactivityTracker = new MeetingInactivityTracker( @@ -118,7 +117,7 @@ class MeetingActor( val expiryTracker = new MeetingExpiryTracker( startedOn = TimeUtil.timeNowInSeconds(), userHasJoined = false, - lastUserLeftOn = 0L, + lastUserLeftOn = None, durationInMinutes = props.durationProps.duration, meetingExpireIfNoUserJoinedInMinutes = props.durationProps.meetingExpireIfNoUserJoinedInMinutes, meetingExpireWhenLastUserLeftInMinutes = props.durationProps.meetingExpireWhenLastUserLeftInMinutes @@ -161,19 +160,21 @@ class MeetingActor( } private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = { - // TODO: Update meeting activity status here - // updateActivityStatus(msg) + state = MeetingInactivityTracker.updateLastActivityTimestamp(state, TimeUtil.timeNowInSeconds()) msg.core match { // Users - case m: ValidateAuthTokenReqMsg => state = usersApp.handleValidateAuthTokenReqMsg(m, state) - case m: UserJoinMeetingReqMsg => state = handleUserJoinMeetingReqMsg(m, state) - case m: UserLeaveReqMsg => state = handleUserLeaveReqMsg(m, state) + case m: ValidateAuthTokenReqMsg => + state = usersApp.handleValidateAuthTokenReqMsg(m, state) + case m: UserJoinMeetingReqMsg => + state = handleUserJoinMeetingReqMsg(m, state) + case m: UserLeaveReqMsg => + state = handleUserLeaveReqMsg(m, state) case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m) case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m) case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m) case m: MeetingActivityResponseCmdMsg => - state = usersApp.handleMeetingActivityResponseCmdMsg(m, state, inactivityTrackerHelper) + state = usersApp.handleMeetingActivityResponseCmdMsg(m, state) case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m) case m: SetRecordingStatusCmdMsg => usersApp.handleSetRecordingStatusCmdMsg(m) case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m) @@ -326,45 +327,8 @@ class MeetingActor( } def handleMonitorNumberOfUsers(msg: MonitorNumberOfUsersInternalMsg) { - state = inactivityTrackerHelper.processMeetingInactivityAudit( - props = liveMeeting.props, - outGW, - eventBus, - state - ) - + state = expiryTrackerHelper.processMeetingInactivityAudit(liveMeeting.props, outGW, eventBus, state) state = expiryTrackerHelper.processMeetingExpiryAudit(liveMeeting.props, state, eventBus) - - monitorNumberOfWebUsers() - monitorNumberOfUsers() - } - - def monitorNumberOfWebUsers() { - - def buildEjectAllFromVoiceConfMsg(meetingId: String, voiceConf: String): BbbCommonEnvCoreMsg = { - val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") - val envelope = BbbCoreEnvelope(EjectAllFromVoiceConfMsg.NAME, routing) - val body = EjectAllFromVoiceConfMsgBody(voiceConf) - val header = BbbCoreHeaderWithMeetingId(EjectAllFromVoiceConfMsg.NAME, meetingId) - val event = EjectAllFromVoiceConfMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - if (Users2x.numUsers(liveMeeting.users2x) == 0 && - state.expiryTracker.lastUserLeftOn > 0) { - if (TimeUtil.timeNowInMinutes - state.expiryTracker.lastUserLeftOn > 2) { - log.info("Empty meeting. Ejecting all users from voice. meetingId={}", props.meetingProp.intId) - val event = buildEjectAllFromVoiceConfMsg(props.meetingProp.intId, props.voiceProp.voiceConf) - outGW.send(event) - } - } - } - - def monitorNumberOfUsers() { - val hasUsers = Users2x.numUsers(liveMeeting.users2x) != 0 - // TODO: We could use a better control over this message to send it just when it really matters :) - eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, UpdateMeetingExpireMonitor(props.meetingProp.intId, hasUsers))) } def handleExtendMeetingDuration(msg: ExtendMeetingDuration) { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala index 3aa361d986..994c23492e 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala @@ -3,9 +3,10 @@ package org.bigbluebutton.core.running import akka.actor.ActorContext import akka.event.Logging import org.bigbluebutton.common2.domain.DefaultProps +import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.bus.IncomingEventBus -import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x } +import org.bigbluebutton.core.domain.{ MeetingEndReason, MeetingExpiryTracker, MeetingInactivityTracker, MeetingState2x } import org.bigbluebutton.core.util.TimeUtil class MeetingExpiryTrackerHelper( @@ -15,45 +16,64 @@ class MeetingExpiryTrackerHelper( val log = Logging(context.system, getClass) - def processNeverBeenJoinedExpiry(nowInSeconds: Long, props: DefaultProps, state: MeetingState2x, - eventBus: IncomingEventBus): MeetingState2x = { - if (MeetingExpiryTracker.hasMeetingExpiredNeverBeenJoined(state, nowInSeconds)) { - log.info("Ending meeting as it has never been joined.") - sendEndMeetingDueToExpiry(props, eventBus) - state - } else { - state - } - } - def processMeetingExpiryAudit(props: DefaultProps, state: MeetingState2x, eventBus: IncomingEventBus): MeetingState2x = { val nowInSeconds = TimeUtil.timeNowInSeconds() - if (!state.expiryTracker.userHasJoined) { - processNeverBeenJoinedExpiry(nowInSeconds, props, state, eventBus) - } else { - if (props.durationProps.duration != 0 && MeetingExpiryTracker.meetingOverDuration(state, nowInSeconds)) { - log.info("Ending meeting as it has passed duration.") - sendEndMeetingDueToExpiry(props, eventBus) + if (MeetingExpiryTracker.hasMeetingExpiredNeverBeenJoined(state, nowInSeconds)) { + sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_WHEN_NOT_JOINED, eventBus, outGW, liveMeeting) + } else if (MeetingExpiryTracker.meetingOverDuration(state, nowInSeconds)) { + sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_AFTER_EXCEEDING_DURATION, eventBus, outGW, liveMeeting) + } else if (MeetingExpiryTracker.hasMeetingExpiredAfterLastUserLeft(state, nowInSeconds)) { + sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_WHEN_LAST_USER_LEFT, eventBus, outGW, liveMeeting) + } + + state + } + + def processMeetingInactivityAudit( + props: DefaultProps, + outGW: OutMessageGateway, + eventBus: IncomingEventBus, + state: MeetingState2x + ): MeetingState2x = { + + val nowInSeconds = TimeUtil.timeNowInSeconds() + if (!MeetingInactivityTracker.hasRecentActivity(state, nowInSeconds)) { + if (MeetingInactivityTracker.isMeetingInactive(state, nowInSeconds)) { + sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_DUE_TO_INACTIVITY, eventBus, outGW, liveMeeting) state } else { - state + if (!MeetingInactivityTracker.warningHasBeenSent(state)) { + warnOfMeetingInactivity(props, outGW, nowInSeconds, state) + MeetingInactivityTracker.setWarningSentAndTimestamp(state, nowInSeconds) + } else { + state + } } + } else { + state } } - def sendEndMeetingDueToExpiry(props: DefaultProps, eventBus: IncomingEventBus): Unit = { + def warnOfMeetingInactivity(props: DefaultProps, outGW: OutMessageGateway, + nowInSeconds: Long, state: MeetingState2x): Unit = { + val timeLeftSeconds = MeetingInactivityTracker.timeLeftInSeconds(state, nowInSeconds) + sendMeetingInactivityWarning(props, outGW, timeLeftSeconds) + } - endMeeting(outGW, liveMeeting) + def sendMeetingInactivityWarning(props: DefaultProps, outGW: OutMessageGateway, timeLeftSeconds: Long): Unit = { + def build(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") + val envelope = BbbCoreEnvelope(MeetingInactivityWarningEvtMsg.NAME, routing) + val body = MeetingInactivityWarningEvtMsgBody(timeLeftInSec) + val header = BbbClientMsgHeader(MeetingInactivityWarningEvtMsg.NAME, meetingId, "not-used") + val event = MeetingInactivityWarningEvtMsg(header, body) - if (liveMeeting.props.meetingProp.isBreakout) { - log.info( - "Informing parent meeting {} that a breakout room has been ended {}", - liveMeeting.props.breakoutProps.parentId, liveMeeting.props.meetingProp.intId - ) - notifyParentThatBreakoutEnded(eventBus, liveMeeting) + BbbCommonEnvCoreMsg(envelope, event) } - destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId) + val event = build(props.meetingProp.intId, timeLeftSeconds) + outGW.send(event) } + } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingInactivityTrackerHelper.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingInactivityTrackerHelper.scala deleted file mode 100755 index eaa592636a..0000000000 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingInactivityTrackerHelper.scala +++ /dev/null @@ -1,100 +0,0 @@ -package org.bigbluebutton.core.running - -import akka.actor.ActorContext -import akka.event.Logging -import org.bigbluebutton.core.domain.{ MeetingInactivityTracker, MeetingState2x } -import com.softwaremill.quicklens._ -import org.bigbluebutton.common2.domain.DefaultProps -import org.bigbluebutton.common2.msgs._ -import org.bigbluebutton.core.OutMessageGateway -import org.bigbluebutton.core.bus.IncomingEventBus -import org.bigbluebutton.core.util.TimeUtil - -class MeetingInactivityTrackerHelper( - val liveMeeting: LiveMeeting, - val outGW: OutMessageGateway -)(implicit val context: ActorContext) extends HandlerHelpers { - - val log = Logging(context.system, getClass) - - def processMeetingInactivityAudit( - props: DefaultProps, - outGW: OutMessageGateway, - eventBus: IncomingEventBus, - state: MeetingState2x - ): MeetingState2x = { - - val nowInSeconds = TimeUtil.timeNowInSeconds() - if (!MeetingInactivityTracker.hasRecentActivity(state, nowInSeconds)) { - if (MeetingInactivityTracker.isMeetingInactive(state, nowInSeconds)) { - sendEndMeetingDueToInactivity(props, eventBus) - state - } else { - if (!MeetingInactivityTracker.warningHasBeenSent(state)) { - warnOfMeetingInactivity(props, outGW, nowInSeconds, state) - MeetingInactivityTracker.setWarningSentAndTimestamp(state, nowInSeconds) - } else { - state - } - } - } else { - state - } - } - - def warnOfMeetingInactivity(props: DefaultProps, outGW: OutMessageGateway, - nowInSeconds: Long, state: MeetingState2x): Unit = { - val timeLeftSeconds = MeetingInactivityTracker.timeLeftInSeconds(state, nowInSeconds) - sendMeetingInactivityWarning(props, outGW, timeLeftSeconds) - } - - def sendEndMeetingDueToInactivity(props: DefaultProps, eventBus: IncomingEventBus): Unit = { - endMeeting(outGW, liveMeeting) - - if (liveMeeting.props.meetingProp.isBreakout) { - log.info( - "Informing parent meeting {} that a breakout room has been ended {}", - liveMeeting.props.breakoutProps.parentId, liveMeeting.props.meetingProp.intId - ) - notifyParentThatBreakoutEnded(eventBus, liveMeeting) - } - - destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId) - } - - def sendMeetingInactivityWarning(props: DefaultProps, outGW: OutMessageGateway, timeLeftSeconds: Long): Unit = { - def build(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") - val envelope = BbbCoreEnvelope(MeetingInactivityWarningEvtMsg.NAME, routing) - val body = MeetingInactivityWarningEvtMsgBody(timeLeftInSec) - val header = BbbClientMsgHeader(MeetingInactivityWarningEvtMsg.NAME, meetingId, "not-used") - val event = MeetingInactivityWarningEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - val event = build(props.meetingProp.intId, timeLeftSeconds) - outGW.send(event) - } - - def processMeetingActivityResponse( - props: DefaultProps, - outGW: OutMessageGateway, - msg: MeetingActivityResponseCmdMsg - ): Unit = { - - def buildMeetingIsActiveEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") - val envelope = BbbCoreEnvelope(MeetingIsActiveEvtMsg.NAME, routing) - val body = MeetingIsActiveEvtMsgBody(meetingId) - val header = BbbClientMsgHeader(MeetingIsActiveEvtMsg.NAME, meetingId, "not-used") - val event = MeetingIsActiveEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - val event = buildMeetingIsActiveEvtMsg(props.meetingProp.intId) - outGW.send(event) - - } -} diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/EndMeetingSysCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/EndMeetingSysCmdMsgHdlr.scala index 597f0e7ed8..1358e94059 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/EndMeetingSysCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/meeting/EndMeetingSysCmdMsgHdlr.scala @@ -3,6 +3,7 @@ package org.bigbluebutton.core2.message.handlers.meeting import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.bus.IncomingEventBus +import org.bigbluebutton.core.domain.MeetingEndReason import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting } trait EndMeetingSysCmdMsgHdlr extends HandlerHelpers { @@ -13,17 +14,7 @@ trait EndMeetingSysCmdMsgHdlr extends HandlerHelpers { val eventBus: IncomingEventBus def handleEndMeeting(msg: EndMeetingSysCmdMsg) { - endMeeting(outGW, liveMeeting) - - if (liveMeeting.props.meetingProp.isBreakout) { - log.info( - "Informing parent meeting {} that a breakout room has been ended {}", - liveMeeting.props.breakoutProps.parentId, liveMeeting.props.meetingProp.intId - ) - notifyParentThatBreakoutEnded(eventBus, liveMeeting) - } - - destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId) + sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_FROM_API, eventBus, outGW, liveMeeting) } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala index cae96d5cd4..00e4297a66 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala @@ -36,4 +36,5 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers { userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false, state) } } + } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala index e817177c33..928ba490a3 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala @@ -227,26 +227,6 @@ object MsgBuilder { BbbCommonEnvCoreMsg(envelope, event) } - def buildMeetingEndingEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") - val envelope = BbbCoreEnvelope(MeetingEndingEvtMsg.NAME, routing) - val body = MeetingEndingEvtMsgBody(meetingId) - val header = BbbClientMsgHeader(MeetingEndingEvtMsg.NAME, meetingId, "not-used") - val event = MeetingEndingEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - def buildMeetingEndedEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { - val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") - val envelope = BbbCoreEnvelope(MeetingEndedEvtMsg.NAME, routing) - val body = MeetingEndedEvtMsgBody(meetingId) - val header = BbbCoreBaseHeader(MeetingEndedEvtMsg.NAME) - val event = MeetingEndedEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - def buildBreakoutRoomEndedEvtMsg(meetingId: String, userId: String, breakoutRoomId: String): BbbCommonEnvCoreMsg = { val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId) val envelope = BbbCoreEnvelope(BreakoutRoomEndedEvtMsg.NAME, routing) diff --git a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/MsgToClientGW.scala b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/MsgToClientGW.scala index bec29b9fa7..ab7e762470 100755 --- a/bbb-apps-common/src/main/scala/org/bigbluebutton/client/MsgToClientGW.scala +++ b/bbb-apps-common/src/main/scala/org/bigbluebutton/client/MsgToClientGW.scala @@ -9,12 +9,10 @@ case class DisconnectAllConnections(scope: String) extends SystemMessage class MsgToClientGW(val connInvokerService: IConnectionInvokerService) { def broadcastToMeeting(msg: BroadcastToMeetingMsg): Unit = { - //println("**** MsgToClientGW broadcastToMeeting " + msg.json) connInvokerService.sendMessage(msg) } def directToClient(msg: DirectToClientMsg): Unit = { - //println("**** MsgToClientGW directToClient " + msg.json) connInvokerService.sendMessage(msg) } diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/SystemMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/SystemMsgs.scala index 755b0cd6f3..82f2aa47c2 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/SystemMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/SystemMsgs.scala @@ -59,7 +59,7 @@ case class MeetingEndedEvtMsgBody(meetingId: String) object MeetingEndingEvtMsg { val NAME = "MeetingEndingEvtMsg"} case class MeetingEndingEvtMsg(header: BbbClientMsgHeader, body: MeetingEndingEvtMsgBody) extends BbbCoreMsg -case class MeetingEndingEvtMsgBody(meetingId: String) +case class MeetingEndingEvtMsgBody(meetingId: String, reason: String) /** diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java index 86bc8b12ae..d15d351dd5 100755 --- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/client/messaging/ConnectionInvokerService.java @@ -150,14 +150,14 @@ public class ConnectionInvokerService implements IConnectionInvokerService { } private void handleCloseMeetingAllConnectionsMsg(CloseMeetingAllConnectionsMsg msg) { + log.info("Disconnecting all clients for meeting {}", msg.meetingId); + IScope meetingScope = getScope(msg.meetingId); if (meetingScope != null) { Set conns = meetingScope.getClientConnections(); for (IConnection conn : conns) { if (conn.isConnected()) { - String connId = (String) conn.getAttribute("INTERNAL_USER_ID"); - log.info("Disconnecting client=[{}] from meeting=[{}]", connId, msg.meetingId); conn.close(); } } From 56ae9ca9a80240ca9aafdbbd54d413960bc1800b Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Wed, 19 Jul 2017 11:40:30 -0700 Subject: [PATCH 05/10] - move handlers to a different package --- .../users/ChangeUserEmojiCmdMsgHdlr.scala | 4 +- .../users/LockUserInMeetingCmdMsgHdlr.scala | 2 +- .../apps}/users/MuteUserCmdMsgHdlr.scala | 2 +- .../apps/users/RegisterUserReqMsgHdlr.scala | 30 +++-- .../users/UserBroadcastCamStartMsgHdlr.scala | 2 +- .../users/UserBroadcastCamStopMsgHdlr.scala | 2 +- .../UserConnectedToGlobalAudioMsgHdlr.scala | 2 +- ...erDisconnectedFromGlobalAudioMsgHdlr.scala | 2 +- .../users/UserJoinMeetingReqMsgHdlr.scala | 2 +- .../core/apps/users/UsersApp.scala | 1 - .../core/apps/users/UsersApp2x.scala | 1 - .../users/ValidateAuthTokenReqMsgHdlr.scala | 105 +++++++++++++++++ .../domain/MeetingInactivityTracker.scala | 12 ++ .../core/models/RegisteredUsers.scala | 10 +- .../core/running/HandlerHelpers.scala | 107 +++++++----------- .../core/running/LiveMeeting.scala | 4 - .../core/running/MeetingActor.scala | 8 +- .../running/MeetingExpiryTrackerHelper.scala | 55 ++++----- .../users/ValidateAuthTokenReqMsgHdlr.scala | 40 ------- .../core2/testdata/FakeUserGenerator.scala | 6 +- 20 files changed, 221 insertions(+), 176 deletions(-) rename akka-bbb-apps/src/main/scala/org/bigbluebutton/{core2/message/handlers => core/apps}/users/ChangeUserEmojiCmdMsgHdlr.scala (91%) rename akka-bbb-apps/src/main/scala/org/bigbluebutton/{core2/message/handlers => core/apps}/users/LockUserInMeetingCmdMsgHdlr.scala (93%) rename akka-bbb-apps/src/main/scala/org/bigbluebutton/{core2/message/handlers => core/apps}/users/MuteUserCmdMsgHdlr.scala (91%) rename akka-bbb-apps/src/main/scala/org/bigbluebutton/{core2/message/handlers => core/apps}/users/UserBroadcastCamStartMsgHdlr.scala (93%) rename akka-bbb-apps/src/main/scala/org/bigbluebutton/{core2/message/handlers => core/apps}/users/UserBroadcastCamStopMsgHdlr.scala (92%) rename akka-bbb-apps/src/main/scala/org/bigbluebutton/{core2/message/handlers => core/apps}/users/UserConnectedToGlobalAudioMsgHdlr.scala (94%) rename akka-bbb-apps/src/main/scala/org/bigbluebutton/{core2/message/handlers => core/apps}/users/UserDisconnectedFromGlobalAudioMsgHdlr.scala (93%) rename akka-bbb-apps/src/main/scala/org/bigbluebutton/{core2/message/handlers => core/apps}/users/UserJoinMeetingReqMsgHdlr.scala (88%) create mode 100755 akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/ValidateAuthTokenReqMsgHdlr.scala delete mode 100755 akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ChangeUserEmojiCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/ChangeUserEmojiCmdMsgHdlr.scala similarity index 91% rename from akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ChangeUserEmojiCmdMsgHdlr.scala rename to akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/ChangeUserEmojiCmdMsgHdlr.scala index 82aa9f6400..1f730cc1f0 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ChangeUserEmojiCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/ChangeUserEmojiCmdMsgHdlr.scala @@ -1,9 +1,9 @@ -package org.bigbluebutton.core2.message.handlers.users +package org.bigbluebutton.core.apps.users import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.models.Users2x -import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting, MeetingActor } +import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting } trait ChangeUserEmojiCmdMsgHdlr { this: BaseMeetingActor => diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/LockUserInMeetingCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/LockUserInMeetingCmdMsgHdlr.scala similarity index 93% rename from akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/LockUserInMeetingCmdMsgHdlr.scala rename to akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/LockUserInMeetingCmdMsgHdlr.scala index bf49e47fc4..2e7545a1d4 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/LockUserInMeetingCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/LockUserInMeetingCmdMsgHdlr.scala @@ -1,4 +1,4 @@ -package org.bigbluebutton.core2.message.handlers.users +package org.bigbluebutton.core.apps.users import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/MuteUserCmdMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MuteUserCmdMsgHdlr.scala similarity index 91% rename from akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/MuteUserCmdMsgHdlr.scala rename to akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MuteUserCmdMsgHdlr.scala index 5cf62f0a8c..9d702fde21 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/MuteUserCmdMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/MuteUserCmdMsgHdlr.scala @@ -1,4 +1,4 @@ -package org.bigbluebutton.core2.message.handlers.users +package org.bigbluebutton.core.apps.users import org.bigbluebutton.common2.msgs.MuteUserCmdMsg import org.bigbluebutton.core.OutMessageGateway diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/RegisterUserReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/RegisterUserReqMsgHdlr.scala index fe55bd6305..73d143a4e0 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/RegisterUserReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/RegisterUserReqMsgHdlr.scala @@ -13,22 +13,7 @@ trait RegisterUserReqMsgHdlr { val outGW: OutMessageGateway 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=" + liveMeeting.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=" + liveMeeting.props.meetingProp.intId - + " userId=" + msg.body.extUserId + " user=" + regUser) - - val event = buildUserRegisteredRespMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.name, regUser.role) - outGW.send(event) - } + log.debug("RECEIVED RegisterUserReqMsg msg {}", msg) def buildUserRegisteredRespMsg(meetingId: String, userId: String, name: String, role: String): BbbCommonEnvCoreMsg = { val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") @@ -38,5 +23,18 @@ trait RegisterUserReqMsgHdlr { val event = UserRegisteredRespMsg(header, body) BbbCommonEnvCoreMsg(envelope, event) } + + 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) + + RegisteredUsers.add(liveMeeting.registeredUsers, regUser) + + log.info("Register user success. meetingId=" + liveMeeting.props.meetingProp.intId + + " userId=" + msg.body.extUserId + " user=" + regUser) + + val event = buildUserRegisteredRespMsg(liveMeeting.props.meetingProp.intId, regUser.id, regUser.name, regUser.role) + outGW.send(event) + } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserBroadcastCamStartMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserBroadcastCamStartMsgHdlr.scala similarity index 93% rename from akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserBroadcastCamStartMsgHdlr.scala rename to akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserBroadcastCamStartMsgHdlr.scala index 9c3d1fa29c..0a700de4e1 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserBroadcastCamStartMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserBroadcastCamStartMsgHdlr.scala @@ -1,4 +1,4 @@ -package org.bigbluebutton.core2.message.handlers.users +package org.bigbluebutton.core.apps.users import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserBroadcastCamStopMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserBroadcastCamStopMsgHdlr.scala similarity index 92% rename from akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserBroadcastCamStopMsgHdlr.scala rename to akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserBroadcastCamStopMsgHdlr.scala index c72c60bb9b..a0fa3df923 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserBroadcastCamStopMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserBroadcastCamStopMsgHdlr.scala @@ -1,4 +1,4 @@ -package org.bigbluebutton.core2.message.handlers.users +package org.bigbluebutton.core.apps.users import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserConnectedToGlobalAudioMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserConnectedToGlobalAudioMsgHdlr.scala similarity index 94% rename from akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserConnectedToGlobalAudioMsgHdlr.scala rename to akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserConnectedToGlobalAudioMsgHdlr.scala index daebb70dec..1217dcff08 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserConnectedToGlobalAudioMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserConnectedToGlobalAudioMsgHdlr.scala @@ -1,4 +1,4 @@ -package org.bigbluebutton.core2.message.handlers.users +package org.bigbluebutton.core.apps.users import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserDisconnectedFromGlobalAudioMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserDisconnectedFromGlobalAudioMsgHdlr.scala similarity index 93% rename from akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserDisconnectedFromGlobalAudioMsgHdlr.scala rename to akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserDisconnectedFromGlobalAudioMsgHdlr.scala index 19aa749e93..8e12cfb3b8 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserDisconnectedFromGlobalAudioMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserDisconnectedFromGlobalAudioMsgHdlr.scala @@ -1,4 +1,4 @@ -package org.bigbluebutton.core2.message.handlers.users +package org.bigbluebutton.core.apps.users import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserJoinMeetingReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala similarity index 88% rename from akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserJoinMeetingReqMsgHdlr.scala rename to akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala index fc08667275..7492c7e2c6 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/UserJoinMeetingReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UserJoinMeetingReqMsgHdlr.scala @@ -1,4 +1,4 @@ -package org.bigbluebutton.core2.message.handlers.users +package org.bigbluebutton.core.apps.users import org.bigbluebutton.common2.msgs.UserJoinMeetingReqMsg import org.bigbluebutton.core.OutMessageGateway diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala index ed9845298e..02e380587a 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp.scala @@ -5,7 +5,6 @@ import akka.event.Logging import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.bus.IncomingEventBus import org.bigbluebutton.core.running.LiveMeeting -import org.bigbluebutton.core2.message.handlers.users.ValidateAuthTokenReqMsgHdlr class UsersApp( val liveMeeting: LiveMeeting, diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp2x.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp2x.scala index 8670a9ff75..389ac48df1 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp2x.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/UsersApp2x.scala @@ -1,7 +1,6 @@ package org.bigbluebutton.core.apps.users import org.bigbluebutton.core.running.MeetingActor -import org.bigbluebutton.core2.message.handlers.users.{ ChangeUserEmojiCmdMsgHdlr, ValidateAuthTokenReqMsgHdlr } trait UsersApp2x extends UserLeaveReqMsgHdlr diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/ValidateAuthTokenReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/ValidateAuthTokenReqMsgHdlr.scala new file mode 100755 index 0000000000..f181b21a21 --- /dev/null +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/ValidateAuthTokenReqMsgHdlr.scala @@ -0,0 +1,105 @@ +package org.bigbluebutton.core.apps.users + +import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.core.OutMessageGateway +import org.bigbluebutton.core.bus.IncomingEventBus +import org.bigbluebutton.core.domain.MeetingState2x +import org.bigbluebutton.core.models._ +import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting } +import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender } + +trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers { + this: UsersApp => + + val liveMeeting: LiveMeeting + val outGW: OutMessageGateway + val eventBus: IncomingEventBus + + def handleValidateAuthTokenReqMsg(msg: ValidateAuthTokenReqMsg, state: MeetingState2x): MeetingState2x = { + log.debug("RECEIVED ValidateAuthTokenReqMsg msg {}", msg) + + RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers) match { + case Some(u) => + val guestPolicyType = GuestsWaiting.getGuestPolicy(liveMeeting.guestsWaiting).policy + if (guestPolicyType == GuestPolicyType.ALWAYS_ACCEPT) { + userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u, state) + } else if (guestPolicyType == GuestPolicyType.ASK_MODERATOR) { + if (u.guest && u.waitingForAcceptance) { + userValidatedButNeedToWaitForApproval(u, state) + } else { + userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u, state) + } + } else { + validateTokenFailed(outGW, meetingId = liveMeeting.props.meetingProp.intId, + userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false, state) + } + case None => + validateTokenFailed(outGW, meetingId = liveMeeting.props.meetingProp.intId, + userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false, state) + } + } + + def validateTokenFailed(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String, + valid: Boolean, waitForApproval: Boolean, state: MeetingState2x): MeetingState2x = { + val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval) + Sender.send(outGW, event) + + // TODO: Should disconnect user here. + + state + } + + def sendValidateAuthTokenRespMsg(meetingId: String, userId: String, authToken: String, + valid: Boolean, waitForApproval: Boolean): Unit = { + val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval) + Sender.send(outGW, event) + } + + def userValidatedButNeedToWaitForApproval(user: RegisteredUser, state: MeetingState2x): MeetingState2x = { + val meetingId = liveMeeting.props.meetingProp.intId + sendValidateAuthTokenRespMsg(meetingId, user.id, user.authToken, valid = true, waitForApproval = false) + + val guest = GuestWaiting(user.id, user.name, user.role) + addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting) + notifyModeratorsOfGuestWaiting(outGW, Vector(guest), liveMeeting.users2x, meetingId) + + state + } + + def addGuestToWaitingForApproval(guest: GuestWaiting, guestsWaitingList: GuestsWaiting): Unit = { + GuestsWaiting.add(guestsWaitingList, guest) + } + + def userValidatedAndNoNeedToWaitForApproval( + outGW: OutMessageGateway, + liveMeeting: LiveMeeting, + user: RegisteredUser, + state: MeetingState2x + ): MeetingState2x = { + + val meetingId = liveMeeting.props.meetingProp.intId + sendValidateAuthTokenRespMsg( + meetingId, + userId = user.id, authToken = user.authToken, valid = true, waitForApproval = false + ) + + // TODO: REMOVE Temp only so we can implement user handling in client. (ralam june 21, 2017) + + sendAllUsersInMeeting(outGW, user.id, liveMeeting) + sendAllVoiceUsersInMeeting(outGW, user.id, liveMeeting.voiceUsers, meetingId) + sendAllWebcamStreams(outGW, user.id, liveMeeting.webcams, meetingId) + val newState = userJoinMeeting(outGW, user.authToken, liveMeeting, state) + if (!Users2x.hasPresenter(liveMeeting.users2x)) { + automaticallyAssignPresenter(outGW, liveMeeting) + } + newState + } + + def notifyModeratorsOfGuestWaiting(outGW: OutMessageGateway, guests: Vector[GuestWaiting], users: Users2x, meetingId: String): Unit = { + val mods = Users2x.findAll(users).filter(p => p.role == Roles.MODERATOR_ROLE) + mods foreach { m => + val event = MsgBuilder.buildGuestsWaitingForApprovalEvtMsg(meetingId, m.intId, guests) + Sender.send(outGW, event) + } + } +} diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala index 248ba33c02..9f6ef48476 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/domain/MeetingInactivityTracker.scala @@ -89,6 +89,18 @@ object MeetingExpiryTracker { state.modify(_.expiryTracker).setTo(tracker) } + def hasMeetingExpired(state: MeetingState2x, timestampInSeconds: Long): (Boolean, Option[String]) = { + if (MeetingExpiryTracker.hasMeetingExpiredNeverBeenJoined(state, timestampInSeconds)) { + (true, Some(MeetingEndReason.ENDED_WHEN_NOT_JOINED)) + } else if (MeetingExpiryTracker.meetingOverDuration(state, timestampInSeconds)) { + (true, Some(MeetingEndReason.ENDED_AFTER_EXCEEDING_DURATION)) + } else if (MeetingExpiryTracker.hasMeetingExpiredAfterLastUserLeft(state, timestampInSeconds)) { + (true, Some(MeetingEndReason.ENDED_WHEN_LAST_USER_LEFT)) + } else { + (false, None) + } + } + def hasMeetingExpiredNeverBeenJoined(state: MeetingState2x, nowInSeconds: Long): Boolean = { !state.expiryTracker.userHasJoined && (nowInSeconds - state.expiryTracker.startedOn > diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/RegisteredUsers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/RegisteredUsers.scala index d5ad3dc5ca..8521900d29 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/RegisteredUsers.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/RegisteredUsers.scala @@ -6,10 +6,8 @@ import com.softwaremill.quicklens._ object RegisteredUsers { def create(userId: String, extId: String, name: String, roles: String, token: String, avatar: String, guest: Boolean, authenticated: Boolean, - waitingForAcceptance: Boolean, users: RegisteredUsers): RegisteredUser = { - val ru = new RegisteredUser(userId, extId, name, roles, token, avatar, guest, authenticated, waitingForAcceptance) - users.save(ru) - ru + waitingForAcceptance: Boolean): RegisteredUser = { + new RegisteredUser(userId, extId, name, roles, token, avatar, guest, authenticated, waitingForAcceptance) } def findWithToken(token: String, users: RegisteredUsers): Option[RegisteredUser] = { @@ -43,6 +41,10 @@ object RegisteredUsers { } yield users.save(regUser) } + def add(users: RegisteredUsers, user: RegisteredUser): Vector[RegisteredUser] = { + users.save(user) + } + def remove(id: String, users: RegisteredUsers): Option[RegisteredUser] = { users.delete(id) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala index 746ac79428..219bdfc941 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala @@ -14,69 +14,6 @@ import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender, UserJoinedM trait HandlerHelpers extends SystemConfiguration { - def validateTokenFailed(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String, - valid: Boolean, waitForApproval: Boolean, state: MeetingState2x): MeetingState2x = { - val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval) - Sender.send(outGW, event) - - // TODO: Should disconnect user here. - - state - } - - def sendValidateAuthTokenRespMsg(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String, - valid: Boolean, waitForApproval: Boolean): Unit = { - val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval) - Sender.send(outGW, event) - } - - def userValidatedButNeedToWaitForApproval(outGW: OutMessageGateway, liveMeeting: LiveMeeting, - user: RegisteredUser, state: MeetingState2x): MeetingState2x = { - val meetingId = liveMeeting.props.meetingProp.intId - sendValidateAuthTokenRespMsg(outGW, meetingId, user.id, user.authToken, valid = true, waitForApproval = false) - - val guest = GuestWaiting(user.id, user.name, user.role) - addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting) - notifyModeratorsOfGuestWaiting(outGW, Vector(guest), liveMeeting.users2x, meetingId) - - state - } - - def addGuestToWaitingForApproval(guest: GuestWaiting, guestsWaitingList: GuestsWaiting): Unit = { - GuestsWaiting.add(guestsWaitingList, guest) - } - - def userValidatedAndNoNeedToWaitForApproval( - outGW: OutMessageGateway, - liveMeeting: LiveMeeting, - user: RegisteredUser, - state: MeetingState2x - ): MeetingState2x = { - - val meetingId = liveMeeting.props.meetingProp.intId - sendValidateAuthTokenRespMsg(outGW, meetingId, - userId = user.id, authToken = user.authToken, valid = true, waitForApproval = false) - - // TODO: REMOVE Temp only so we can implement user handling in client. (ralam june 21, 2017) - - sendAllUsersInMeeting(outGW, user.id, liveMeeting) - sendAllVoiceUsersInMeeting(outGW, user.id, liveMeeting.voiceUsers, meetingId) - sendAllWebcamStreams(outGW, user.id, liveMeeting.webcams, meetingId) - val newState = userJoinMeeting(outGW, user.authToken, liveMeeting, state) - if (!Users2x.hasPresenter(liveMeeting.users2x)) { - automaticallyAssignPresenter(outGW, liveMeeting) - } - newState - } - - def notifyModeratorsOfGuestWaiting(outGW: OutMessageGateway, guests: Vector[GuestWaiting], users: Users2x, meetingId: String): Unit = { - val mods = Users2x.findAll(users).filter(p => p.role == Roles.MODERATOR_ROLE) - mods foreach { m => - val event = MsgBuilder.buildGuestsWaitingForApprovalEvtMsg(meetingId, m.intId, guests) - Sender.send(outGW, event) - } - } - def sendAllUsersInMeeting(outGW: OutMessageGateway, requesterId: String, liveMeeting: LiveMeeting): Unit = { val meetingId = liveMeeting.props.meetingProp.intId val users = Users2x.findAll(liveMeeting.users2x) @@ -236,4 +173,48 @@ trait HandlerHelpers extends SystemConfiguration { ejectAllUsersFromVoiceConf(outGW, liveMeeting) destroyMeeting(eventBus, liveMeeting.props.meetingProp.intId) } + + def sendEndMeetingDueToExpiry2(reason: String, eventBus: IncomingEventBus, outGW: OutMessageGateway, liveMeeting: LiveMeeting): Unit = { + val meetingId = liveMeeting.props.meetingProp.intId + + val endMeetingEvt = buildMeetingEndingEvtMsg(reason, meetingId) + outGW.send(endMeetingEvt) + + val endedEvt = buildMeetingEndedEvtMsg(meetingId) + outGW.send(endedEvt) + + if (liveMeeting.props.meetingProp.isBreakout) { + eventBus.publish(BigBlueButtonEvent( + liveMeeting.props.breakoutProps.parentId, + new BreakoutRoomEndedInternalMsg(meetingId) + )) + } + + val event = MsgBuilder.buildEjectAllFromVoiceConfMsg(meetingId, liveMeeting.props.voiceProp.voiceConf) + outGW.send(event) + + eventBus.publish(BigBlueButtonEvent(meetingManagerChannel, new DestroyMeetingInternalMsg(meetingId))) + + MeetingStatus2x.meetingHasEnded(liveMeeting.status) + } + + def buildMeetingEndingEvtMsg(reason: String, meetingId: String): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") + val envelope = BbbCoreEnvelope(MeetingEndingEvtMsg.NAME, routing) + val body = MeetingEndingEvtMsgBody(meetingId, reason) + val header = BbbClientMsgHeader(MeetingEndingEvtMsg.NAME, meetingId, "not-used") + val event = MeetingEndingEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + def buildMeetingEndedEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { + val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") + val envelope = BbbCoreEnvelope(MeetingEndedEvtMsg.NAME, routing) + val body = MeetingEndedEvtMsgBody(meetingId) + val header = BbbCoreBaseHeader(MeetingEndedEvtMsg.NAME) + val event = MeetingEndedEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/LiveMeeting.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/LiveMeeting.scala index 55a5118060..b09d6ffc00 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/LiveMeeting.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/LiveMeeting.scala @@ -28,10 +28,6 @@ class LiveMeeting( val guestsWaiting: GuestsWaiting ) { - def hasMeetingEnded(): Boolean = { - MeetingStatus2x.hasMeetingEnded(status) - } - def lockLayout(lock: Boolean) { MeetingStatus2x.lockLayout(status, lock) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala index 3490373742..785ee3885b 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala @@ -2,7 +2,7 @@ package org.bigbluebutton.core.running import java.io.{ PrintWriter, StringWriter } -import org.bigbluebutton.core.apps.users.UsersApp +import org.bigbluebutton.core.apps.users._ import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingInactivityTracker, MeetingState2x } import org.bigbluebutton.core.util.TimeUtil //import java.util.concurrent.TimeUnit @@ -104,7 +104,7 @@ class MeetingActor( val chatApp2x = new ChatApp2x(liveMeeting, outGW) val usersApp = new UsersApp(liveMeeting, outGW, eventBus) - val expiryTrackerHelper = new MeetingExpiryTrackerHelper(liveMeeting, outGW) + val expiryTrackerHelper = new MeetingExpiryTrackerHelper(liveMeeting, outGW, eventBus) val inactivityTracker = new MeetingInactivityTracker( props.durationProps.maxInactivityTimeoutMinutes, @@ -327,8 +327,8 @@ class MeetingActor( } def handleMonitorNumberOfUsers(msg: MonitorNumberOfUsersInternalMsg) { - state = expiryTrackerHelper.processMeetingInactivityAudit(liveMeeting.props, outGW, eventBus, state) - state = expiryTrackerHelper.processMeetingExpiryAudit(liveMeeting.props, state, eventBus) + state = expiryTrackerHelper.processMeetingInactivityAudit(state) + state = expiryTrackerHelper.processMeetingExpiryAudit(state) } def handleExtendMeetingDuration(msg: ExtendMeetingDuration) { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala index 994c23492e..c63f900555 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingExpiryTrackerHelper.scala @@ -11,31 +11,28 @@ import org.bigbluebutton.core.util.TimeUtil class MeetingExpiryTrackerHelper( val liveMeeting: LiveMeeting, - val outGW: OutMessageGateway + val outGW: OutMessageGateway, + val eventBus: IncomingEventBus )(implicit val context: ActorContext) extends HandlerHelpers { val log = Logging(context.system, getClass) - def processMeetingExpiryAudit(props: DefaultProps, state: MeetingState2x, eventBus: IncomingEventBus): MeetingState2x = { + def processMeetingExpiryAudit(state: MeetingState2x): MeetingState2x = { val nowInSeconds = TimeUtil.timeNowInSeconds() - if (MeetingExpiryTracker.hasMeetingExpiredNeverBeenJoined(state, nowInSeconds)) { - sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_WHEN_NOT_JOINED, eventBus, outGW, liveMeeting) - } else if (MeetingExpiryTracker.meetingOverDuration(state, nowInSeconds)) { - sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_AFTER_EXCEEDING_DURATION, eventBus, outGW, liveMeeting) - } else if (MeetingExpiryTracker.hasMeetingExpiredAfterLastUserLeft(state, nowInSeconds)) { - sendEndMeetingDueToExpiry(MeetingEndReason.ENDED_WHEN_LAST_USER_LEFT, eventBus, outGW, liveMeeting) + val (expired, reason) = MeetingExpiryTracker.hasMeetingExpired(state, nowInSeconds) + if (expired) { + for { + expireReason <- reason + } yield { + sendEndMeetingDueToExpiry(expireReason, eventBus, outGW, liveMeeting) + } } state } - def processMeetingInactivityAudit( - props: DefaultProps, - outGW: OutMessageGateway, - eventBus: IncomingEventBus, - state: MeetingState2x - ): MeetingState2x = { + def processMeetingInactivityAudit(state: MeetingState2x): MeetingState2x = { val nowInSeconds = TimeUtil.timeNowInSeconds() if (!MeetingInactivityTracker.hasRecentActivity(state, nowInSeconds)) { @@ -44,7 +41,7 @@ class MeetingExpiryTrackerHelper( state } else { if (!MeetingInactivityTracker.warningHasBeenSent(state)) { - warnOfMeetingInactivity(props, outGW, nowInSeconds, state) + warnOfMeetingInactivity(nowInSeconds, state) MeetingInactivityTracker.setWarningSentAndTimestamp(state, nowInSeconds) } else { state @@ -55,25 +52,19 @@ class MeetingExpiryTrackerHelper( } } - def warnOfMeetingInactivity(props: DefaultProps, outGW: OutMessageGateway, - nowInSeconds: Long, state: MeetingState2x): Unit = { + def warnOfMeetingInactivity(nowInSeconds: Long, state: MeetingState2x): Unit = { val timeLeftSeconds = MeetingInactivityTracker.timeLeftInSeconds(state, nowInSeconds) - sendMeetingInactivityWarning(props, outGW, timeLeftSeconds) - } - - def sendMeetingInactivityWarning(props: DefaultProps, outGW: OutMessageGateway, timeLeftSeconds: Long): Unit = { - def build(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = { - val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") - val envelope = BbbCoreEnvelope(MeetingInactivityWarningEvtMsg.NAME, routing) - val body = MeetingInactivityWarningEvtMsgBody(timeLeftInSec) - val header = BbbClientMsgHeader(MeetingInactivityWarningEvtMsg.NAME, meetingId, "not-used") - val event = MeetingInactivityWarningEvtMsg(header, body) - - BbbCommonEnvCoreMsg(envelope, event) - } - - val event = build(props.meetingProp.intId, timeLeftSeconds) + val event = buildMeetingInactivityWarningEvtMsg(liveMeeting.props.meetingProp.intId, timeLeftSeconds) outGW.send(event) } + def buildMeetingInactivityWarningEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") + val envelope = BbbCoreEnvelope(MeetingInactivityWarningEvtMsg.NAME, routing) + val body = MeetingInactivityWarningEvtMsgBody(timeLeftInSec) + val header = BbbClientMsgHeader(MeetingInactivityWarningEvtMsg.NAME, meetingId, "not-used") + val event = MeetingInactivityWarningEvtMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala deleted file mode 100755 index 00e4297a66..0000000000 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/users/ValidateAuthTokenReqMsgHdlr.scala +++ /dev/null @@ -1,40 +0,0 @@ -package org.bigbluebutton.core2.message.handlers.users - -import org.bigbluebutton.common2.msgs._ -import org.bigbluebutton.core.OutMessageGateway -import org.bigbluebutton.core.apps.users.UsersApp -import org.bigbluebutton.core.domain.MeetingState2x -import org.bigbluebutton.core.models._ -import org.bigbluebutton.core.running.{ HandlerHelpers, LiveMeeting } - -trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers { - this: UsersApp => - - val liveMeeting: LiveMeeting - val outGW: OutMessageGateway - - def handleValidateAuthTokenReqMsg(msg: ValidateAuthTokenReqMsg, state: MeetingState2x): MeetingState2x = { - log.debug("RECEIVED ValidateAuthTokenReqMsg msg {}", msg) - - RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers) match { - case Some(u) => - val guestPolicyType = GuestsWaiting.getGuestPolicy(liveMeeting.guestsWaiting).policy - if (guestPolicyType == GuestPolicyType.ALWAYS_ACCEPT) { - userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u, state) - } else if (guestPolicyType == GuestPolicyType.ASK_MODERATOR) { - if (u.guest && u.waitingForAcceptance) { - userValidatedButNeedToWaitForApproval(outGW, liveMeeting, u, state) - } else { - userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u, state) - } - } else { - validateTokenFailed(outGW, meetingId = liveMeeting.props.meetingProp.intId, - userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false, state) - } - case None => - validateTokenFailed(outGW, meetingId = liveMeeting.props.meetingProp.intId, - userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false, state) - } - } - -} diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/testdata/FakeUserGenerator.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/testdata/FakeUserGenerator.scala index 232df4f823..407b095465 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/testdata/FakeUserGenerator.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/testdata/FakeUserGenerator.scala @@ -53,8 +53,10 @@ object FakeUserGenerator { val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" + RandomStringGenerator.randomAlphanumericString(10) + ".png" - RegisteredUsers.create(userId = id, extId, name, role, - authToken, avatarURL, guest, authed, waitingForAcceptance = true, users) + val ru = RegisteredUsers.create(userId = id, extId, name, role, + authToken, avatarURL, guest, authed, waitingForAcceptance = true) + RegisteredUsers.add(users, ru) + ru } def createFakeVoiceUser(user: RegisteredUser, callingWith: String, muted: Boolean, talking: Boolean, From d488ff745d7c528f612bc312532d67e5664f988e Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Wed, 19 Jul 2017 14:59:36 -0700 Subject: [PATCH 06/10] - implement guest policy --- .../bigbluebutton/core/apps/GuestsApp.scala | 5 +- .../users/GetUsersMeetingReqMsgHdlr.scala | 4 +- .../users/ValidateAuthTokenReqMsgHdlr.scala | 74 +++++++++++++------ .../senders/ReceivedJsonMsgHandlerActor.scala | 10 +++ .../core/running/HandlerHelpers.scala | 28 ------- .../core/running/LiveMeeting.scala | 2 - .../core/running/MeetingActor.scala | 4 +- .../guests/GetGuestPolicyReqMsgHdlr.scala | 16 ++++ .../guests/SetGuestPolicyMsgHdlr.scala | 4 +- .../common2/msgs/GuestsMsgs.scala | 24 ++++-- .../modules/users/services/MessageReceiver.as | 26 ++----- .../modules/users/services/MessageSender.as | 2 +- 12 files changed, 109 insertions(+), 90 deletions(-) create mode 100755 akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GetGuestPolicyReqMsgHdlr.scala diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/GuestsApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/GuestsApp.scala index 30d5d63465..b540b32228 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/GuestsApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/GuestsApp.scala @@ -1,11 +1,12 @@ package org.bigbluebutton.core.apps import org.bigbluebutton.core.running.MeetingActor -import org.bigbluebutton.core2.message.handlers.guests.{ GetGuestsWaitingApprovalReqMsgHdlr, GuestsWaitingApprovedMsgHdlr, SetGuestPolicyMsgHdlr } +import org.bigbluebutton.core2.message.handlers.guests.{ GetGuestPolicyReqMsgHdlr, GetGuestsWaitingApprovalReqMsgHdlr, GuestsWaitingApprovedMsgHdlr, SetGuestPolicyMsgHdlr } trait GuestsApp extends GetGuestsWaitingApprovalReqMsgHdlr with GuestsWaitingApprovedMsgHdlr - with SetGuestPolicyMsgHdlr { + with SetGuestPolicyMsgHdlr + with GetGuestPolicyReqMsgHdlr { this: MeetingActor => diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/GetUsersMeetingReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/GetUsersMeetingReqMsgHdlr.scala index 9f3ce9eb32..6abcc90c1e 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/GetUsersMeetingReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/GetUsersMeetingReqMsgHdlr.scala @@ -11,8 +11,8 @@ trait GetUsersMeetingReqMsgHdlr extends HandlerHelpers { val outGW: OutMessageGateway def handleGetUsersMeetingReqMsg(msg: GetUsersMeetingReqMsg): Unit = { - sendAllUsersInMeeting(outGW, msg.body.userId, liveMeeting) - sendAllVoiceUsersInMeeting(outGW, msg.body.userId, liveMeeting.voiceUsers, liveMeeting.props.meetingProp.intId) + sendAllUsersInMeeting(msg.body.userId) + sendAllVoiceUsersInMeeting(msg.body.userId, liveMeeting.voiceUsers, liveMeeting.props.meetingProp.intId) sendAllWebcamStreams(outGW, msg.body.userId, liveMeeting.webcams, liveMeeting.props.meetingProp.intId) } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/ValidateAuthTokenReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/ValidateAuthTokenReqMsgHdlr.scala index f181b21a21..908464381f 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/ValidateAuthTokenReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/users/ValidateAuthTokenReqMsgHdlr.scala @@ -18,20 +18,14 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers { def handleValidateAuthTokenReqMsg(msg: ValidateAuthTokenReqMsg, state: MeetingState2x): MeetingState2x = { log.debug("RECEIVED ValidateAuthTokenReqMsg msg {}", msg) - RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers) match { + val regUser = RegisteredUsers.getRegisteredUserWithToken(msg.body.authToken, msg.body.userId, liveMeeting.registeredUsers) + + regUser match { case Some(u) => - val guestPolicyType = GuestsWaiting.getGuestPolicy(liveMeeting.guestsWaiting).policy - if (guestPolicyType == GuestPolicyType.ALWAYS_ACCEPT) { - userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u, state) - } else if (guestPolicyType == GuestPolicyType.ASK_MODERATOR) { - if (u.guest && u.waitingForAcceptance) { - userValidatedButNeedToWaitForApproval(u, state) - } else { - userValidatedAndNoNeedToWaitForApproval(outGW, liveMeeting, u, state) - } + if (noNeedForApproval(u)) { + userValidatedAndNoNeedToWaitForApproval(u, state) } else { - validateTokenFailed(outGW, meetingId = liveMeeting.props.meetingProp.intId, - userId = msg.body.userId, authToken = msg.body.authToken, valid = false, waitForApproval = false, state) + goThroughGuestPolicy(liveMeeting.guestsWaiting, u, state) } case None => validateTokenFailed(outGW, meetingId = liveMeeting.props.meetingProp.intId, @@ -39,6 +33,24 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers { } } + def noNeedForApproval(user: RegisteredUser): Boolean = { + !user.guest || (user.guest && !user.waitingForAcceptance) + } + + def goThroughGuestPolicy(guestsWaiting: GuestsWaiting, user: RegisteredUser, state: MeetingState2x): MeetingState2x = { + if (doesNotHaveToWaitForApproval(guestsWaiting, user)) { + userValidatedAndNoNeedToWaitForApproval(user, state) + } else { + userValidatedButNeedToWaitForApproval(user, state) + } + } + + def doesNotHaveToWaitForApproval(guestsWaiting: GuestsWaiting, user: RegisteredUser): Boolean = { + val guestPolicyType = GuestsWaiting.getGuestPolicy(guestsWaiting).policy + (guestPolicyType == GuestPolicyType.ALWAYS_ACCEPT) || + (guestPolicyType == GuestPolicyType.ASK_MODERATOR && user.guest && !user.waitingForAcceptance) + } + def validateTokenFailed(outGW: OutMessageGateway, meetingId: String, userId: String, authToken: String, valid: Boolean, waitForApproval: Boolean, state: MeetingState2x): MeetingState2x = { val event = MsgBuilder.buildValidateAuthTokenRespMsg(meetingId, userId, authToken, valid, waitForApproval) @@ -61,7 +73,7 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers { val guest = GuestWaiting(user.id, user.name, user.role) addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting) - notifyModeratorsOfGuestWaiting(outGW, Vector(guest), liveMeeting.users2x, meetingId) + notifyModeratorsOfGuestWaiting(Vector(guest), liveMeeting.users2x, meetingId) state } @@ -70,12 +82,7 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers { GuestsWaiting.add(guestsWaitingList, guest) } - def userValidatedAndNoNeedToWaitForApproval( - outGW: OutMessageGateway, - liveMeeting: LiveMeeting, - user: RegisteredUser, - state: MeetingState2x - ): MeetingState2x = { + def userValidatedAndNoNeedToWaitForApproval(user: RegisteredUser, state: MeetingState2x): MeetingState2x = { val meetingId = liveMeeting.props.meetingProp.intId sendValidateAuthTokenRespMsg( @@ -85,8 +92,8 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers { // TODO: REMOVE Temp only so we can implement user handling in client. (ralam june 21, 2017) - sendAllUsersInMeeting(outGW, user.id, liveMeeting) - sendAllVoiceUsersInMeeting(outGW, user.id, liveMeeting.voiceUsers, meetingId) + sendAllUsersInMeeting(user.id) + sendAllVoiceUsersInMeeting(user.id, liveMeeting.voiceUsers, meetingId) sendAllWebcamStreams(outGW, user.id, liveMeeting.webcams, meetingId) val newState = userJoinMeeting(outGW, user.authToken, liveMeeting, state) if (!Users2x.hasPresenter(liveMeeting.users2x)) { @@ -95,7 +102,30 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers { newState } - def notifyModeratorsOfGuestWaiting(outGW: OutMessageGateway, guests: Vector[GuestWaiting], users: Users2x, meetingId: String): Unit = { + def sendAllUsersInMeeting(requesterId: String): Unit = { + val meetingId = liveMeeting.props.meetingProp.intId + val users = Users2x.findAll(liveMeeting.users2x) + val webUsers = users.map { u => + WebUser(intId = u.intId, extId = u.extId, name = u.name, role = u.role, + guest = u.guest, authed = u.authed, waitingForAcceptance = u.waitingForAcceptance, emoji = u.emoji, + locked = u.locked, presenter = u.presenter, avatar = u.avatar) + } + + val event = MsgBuilder.buildGetUsersMeetingRespMsg(meetingId, requesterId, webUsers) + Sender.send(outGW, event) + } + + def sendAllVoiceUsersInMeeting(requesterId: String, voiceUsers: VoiceUsers, meetingId: String): Unit = { + val vu = VoiceUsers.findAll(voiceUsers).map { u => + VoiceConfUser(intId = u.intId, voiceUserId = u.voiceUserId, callingWith = u.callingWith, callerName = u.callerName, + callerNum = u.callerNum, muted = u.muted, talking = u.talking, listenOnly = u.listenOnly) + } + + val event = MsgBuilder.buildGetVoiceUsersMeetingRespMsg(meetingId, requesterId, vu) + Sender.send(outGW, event) + } + + def notifyModeratorsOfGuestWaiting(guests: Vector[GuestWaiting], users: Users2x, meetingId: String): Unit = { val mods = Users2x.findAll(users).filter(p => p.role == Roles.MODERATOR_ROLE) mods foreach { m => val event = MsgBuilder.buildGuestsWaitingForApprovalEvtMsg(meetingId, m.intId, guests) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala index 21f869d281..79e142c565 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala @@ -71,6 +71,16 @@ class ReceivedJsonMsgHandlerActor( case DestroyMeetingSysCmdMsg.NAME => route[DestroyMeetingSysCmdMsg](meetingManagerChannel, envelope, jsonNode) + // Guests + case GetGuestsWaitingApprovalReqMsg.NAME => + routeGenericMsg[GetGuestsWaitingApprovalReqMsg](envelope, jsonNode) + case GuestsWaitingApprovedMsg.NAME => + routeGenericMsg[GuestsWaitingApprovedMsg](envelope, jsonNode) + case SetGuestPolicyCmdMsg.NAME => + routeGenericMsg[SetGuestPolicyCmdMsg](envelope, jsonNode) + case GetGuestPolicyReqMsg.NAME => + routeGenericMsg[GetGuestPolicyReqMsg](envelope, jsonNode) + // Users case GetUsersMeetingReqMsg.NAME => routeGenericMsg[GetUsersMeetingReqMsg](envelope, jsonNode) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala index 219bdfc941..09e6d6a172 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/HandlerHelpers.scala @@ -1,32 +1,17 @@ package org.bigbluebutton.core.running import org.bigbluebutton.SystemConfiguration -import org.bigbluebutton.common2.domain.DefaultProps import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.api.{ BreakoutRoomEndedInternalMsg, DestroyMeetingInternalMsg, RecordingStatusChanged } import org.bigbluebutton.core.bus.{ BigBlueButtonEvent, IncomingEventBus } import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingState2x } import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.models._ -import org.bigbluebutton.core.util.TimeUtil import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender, UserJoinedMeetingEvtMsgBuilder } trait HandlerHelpers extends SystemConfiguration { - def sendAllUsersInMeeting(outGW: OutMessageGateway, requesterId: String, liveMeeting: LiveMeeting): Unit = { - val meetingId = liveMeeting.props.meetingProp.intId - val users = Users2x.findAll(liveMeeting.users2x) - val webUsers = users.map { u => - WebUser(intId = u.intId, extId = u.extId, name = u.name, role = u.role, - guest = u.guest, authed = u.authed, waitingForAcceptance = u.waitingForAcceptance, emoji = u.emoji, - locked = u.locked, presenter = u.presenter, avatar = u.avatar) - } - - val event = MsgBuilder.buildGetUsersMeetingRespMsg(meetingId, requesterId, webUsers) - Sender.send(outGW, event) - } - def sendAllWebcamStreams(outGW: OutMessageGateway, requesterId: String, webcams: Webcams, meetingId: String): Unit = { val streams = org.bigbluebutton.core.models.Webcams.findAll(webcams) val webcamStreams = streams.map { u => @@ -40,19 +25,6 @@ trait HandlerHelpers extends SystemConfiguration { Sender.send(outGW, event) } - def sendAllVoiceUsersInMeeting(outGW: OutMessageGateway, requesterId: String, - voiceUsers: VoiceUsers, - meetingId: String): Unit = { - - val vu = VoiceUsers.findAll(voiceUsers).map { u => - VoiceConfUser(intId = u.intId, voiceUserId = u.voiceUserId, callingWith = u.callingWith, callerName = u.callerName, - callerNum = u.callerNum, muted = u.muted, talking = u.talking, listenOnly = u.listenOnly) - } - - val event = MsgBuilder.buildGetVoiceUsersMeetingRespMsg(meetingId, requesterId, vu) - Sender.send(outGW, event) - } - def userJoinMeeting(outGW: OutMessageGateway, authToken: String, liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = { val nu = for { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/LiveMeeting.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/LiveMeeting.scala index b09d6ffc00..7330004088 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/LiveMeeting.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/LiveMeeting.scala @@ -1,7 +1,5 @@ package org.bigbluebutton.core.running -import java.util.concurrent.TimeUnit - import org.bigbluebutton.common2.domain.DefaultProps import org.bigbluebutton.core.api._ import org.bigbluebutton.core.apps._ diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala index 785ee3885b..554f5c37a2 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala @@ -256,9 +256,9 @@ class MeetingActor( // Guests case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m) - case m: SetGuestPolicyMsg => handleSetGuestPolicyMsg(m) + case m: SetGuestPolicyCmdMsg => handleSetGuestPolicyMsg(m) case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m) - + case m: GetGuestPolicyReqMsg => handleGetGuestPolicyReqMsg(m) // Chat case m: GetChatHistoryReqMsg => chatApp2x.handleGetChatHistoryReqMsg(m) case m: SendPublicMessagePubMsg => chatApp2x.handleSendPublicMessagePubMsg(m) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GetGuestPolicyReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GetGuestPolicyReqMsgHdlr.scala new file mode 100755 index 0000000000..91772071bd --- /dev/null +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GetGuestPolicyReqMsgHdlr.scala @@ -0,0 +1,16 @@ +package org.bigbluebutton.core2.message.handlers.guests + +import org.bigbluebutton.common2.msgs.GetGuestPolicyReqMsg +import org.bigbluebutton.core.OutMessageGateway +import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting } + +trait GetGuestPolicyReqMsgHdlr { + this: BaseMeetingActor => + + val liveMeeting: LiveMeeting + val outGW: OutMessageGateway + + def handleGetGuestPolicyReqMsg(msg: GetGuestPolicyReqMsg): Unit = { + + } +} diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/SetGuestPolicyMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/SetGuestPolicyMsgHdlr.scala index a09db586bf..6be3d9b0f5 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/SetGuestPolicyMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/SetGuestPolicyMsgHdlr.scala @@ -1,6 +1,6 @@ package org.bigbluebutton.core2.message.handlers.guests -import org.bigbluebutton.common2.msgs.SetGuestPolicyMsg +import org.bigbluebutton.common2.msgs.{ SetGuestPolicyCmdMsg } import org.bigbluebutton.core.OutMessageGateway import org.bigbluebutton.core.models.{ GuestPolicy, GuestPolicyType, GuestWaiting, GuestsWaiting } import org.bigbluebutton.core.running.{ BaseMeetingActor, HandlerHelpers, LiveMeeting } @@ -12,7 +12,7 @@ trait SetGuestPolicyMsgHdlr { val liveMeeting: LiveMeeting val outGW: OutMessageGateway - def handleSetGuestPolicyMsg(msg: SetGuestPolicyMsg): Unit = { + def handleSetGuestPolicyMsg(msg: SetGuestPolicyCmdMsg): Unit = { val newPolicy = msg.body.policy.toUpperCase() if (GuestPolicyType.policyTypes.contains(newPolicy)) { val policy = GuestPolicy(newPolicy, msg.body.setBy) diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala index 1f4c41a777..ec0131d42a 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala @@ -7,7 +7,7 @@ object GetGuestsWaitingApprovalReqMsg { val NAME = "GetGuestsWaitingApprovalReqMsg" } case class GetGuestsWaitingApprovalReqMsg(header: BbbClientMsgHeader, - body: GetGuestsWaitingApprovalReqMsgBody) extends BbbCoreMsg + body: GetGuestsWaitingApprovalReqMsgBody) extends StandardMsg case class GetGuestsWaitingApprovalReqMsgBody(requesterId: String) @@ -47,7 +47,7 @@ object GuestsWaitingApprovedMsg { } case class GuestsWaitingApprovedMsg(header: BbbClientMsgHeader, - body: GuestsWaitingApprovedMsgBody) extends BbbCoreMsg + body: GuestsWaitingApprovedMsgBody) extends StandardMsg case class GuestsWaitingApprovedMsgBody(guests: Vector[GuestApprovedVO], approvedBy: String) case class GuestApprovedVO(guest: String, approved: Boolean) @@ -76,12 +76,12 @@ case class GuestApprovedEvtMsgBody(approved: Boolean, approvedBy: String) /** * Message from user to set the guest policy. */ -object SetGuestPolicyMsg { - val NAME = "SetGuestPolicyMsg" +object SetGuestPolicyCmdMsg { + val NAME = "SetGuestPolicyCmdMsg" } -case class SetGuestPolicyMsg(header: BbbClientMsgHeader, - body: SetGuestPolicyMsgBody) extends BbbCoreMsg -case class SetGuestPolicyMsgBody(policy: String, setBy: String) +case class SetGuestPolicyCmdMsg(header: BbbClientMsgHeader, + body: SetGuestPolicyCmdMsgBody) extends StandardMsg +case class SetGuestPolicyCmdMsgBody(policy: String, setBy: String) /** * Message sent to all clients that guest policy has been changed. @@ -94,6 +94,14 @@ case class GuestPolicyChangedEvtMsg(header: BbbClientMsgHeader, case class GuestPolicyChangedEvtMsgBody(policy: String, setBy: String) - +/** + * Message from user to get the guest policy. + */ +object GetGuestPolicyReqMsg { + val NAME = "GetGuestPolicyReqMsg" +} +case class GetGuestPolicyReqMsg(header: BbbClientMsgHeader, + body: GetGuestPolicyReqMsgBody) extends StandardMsg +case class GetGuestPolicyReqMsgBody(reqeustedBy: String) diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as index 08b0d4cb95..f2b8bdd935 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as @@ -112,15 +112,9 @@ package org.bigbluebutton.modules.users.services case "GuestsWaitingForApprovalEvtMsg": handleGuestsWaitingForApprovalEvtMsg(message); break; - case "meetingEnded": - handleLogout(message); - break; case "MeetingEndingEvtMsg": handleMeetingEnding(message); break; - case "meetingHasEnded": - handleMeetingHasEnded(message); - break; case "meetingMuted": handleMeetingMuted(message); break; @@ -274,6 +268,7 @@ package org.bigbluebutton.modules.users.services var guestWaiting: GuestWaiting = new GuestWaiting(guest.intId, guest.name, guest.role); LiveMeeting.inst().guestsWaiting.add(guestWaiting); } + private function handleGuestsWaitingForApprovalEvtMsg(msg: Object): void { var body: Object = msg.body as Object; var guests: Array = body.guests as Array; @@ -473,10 +468,6 @@ package org.bigbluebutton.modules.users.services return; } - - private function handleMeetingHasEnded(msg: Object):void { - LOGGER.debug("*** handleMeetingHasEnded {0} **** \n", [msg.msg]); - } private function handlePermissionsSettingsChanged(msg:Object):void { //LOGGER.debug("handlePermissionsSettingsChanged {0} \n", [msg.msg]); @@ -565,23 +556,16 @@ package org.bigbluebutton.modules.users.services } - - - /** - * Called by the server to tell the client that the meeting has ended. - */ - public function handleLogout(msg:Object):void { - var endMeetingEvent:BBBEvent = new BBBEvent(BBBEvent.END_MEETING_EVENT); - dispatcher.dispatchEvent(endMeetingEvent); - } /** * This meeting is in the process of ending by the server */ public function handleMeetingEnding(msg:Object):void { // Avoid trying to reconnect - var endMeetingEvent:BBBEvent = new BBBEvent(BBBEvent.CANCEL_RECONNECTION_EVENT); - dispatcher.dispatchEvent(endMeetingEvent); + var cancelReconnectEvent:BBBEvent = new BBBEvent(BBBEvent.CANCEL_RECONNECTION_EVENT); + dispatcher.dispatchEvent(cancelReconnectEvent); + var endMeetingEvent:BBBEvent = new BBBEvent(BBBEvent.END_MEETING_EVENT); + dispatcher.dispatchEvent(endMeetingEvent); } public function handleAssignPresenterCallback(msg:Object):void { diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as index b15e13ca56..9230591b84 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as @@ -594,7 +594,7 @@ package org.bigbluebutton.modules.users.services public function setGuestPolicy(policy:String):void { LOGGER.debug("setGuestPolicy - new policy:[" + policy + "]"); var message:Object = { - header: {name: "SetGuestPolicyMsg", meetingId: UsersUtil.getInternalMeetingID(), + header: {name: "SetGuestPolicyCmdMsg", meetingId: UsersUtil.getInternalMeetingID(), userId: UsersUtil.getMyUserID()}, body: {policy: policy, setBy: UsersUtil.getMyUserID()} }; From d7e1a230228bc437a486db1b592bd947a4b8bf2d Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Wed, 19 Jul 2017 19:24:43 -0700 Subject: [PATCH 07/10] - continue implement guest policy --- .../core/running/MeetingActor.scala | 4 +- .../guests/GetGuestPolicyReqMsgHdlr.scala | 26 +++- .../core2/testdata/FakeTestData.scala | 27 ++-- .../common2/msgs/GuestsMsgs.scala | 14 +- .../core/model/users/GuestsApp.as | 12 ++ .../bigbluebutton/main/views/GuestWindow.mxml | 34 +++-- .../main/views/MainApplicationShell.mxml | 133 +++++++++--------- .../modules/users/services/MessageReceiver.as | 32 +++-- .../modules/users/services/MessageSender.as | 12 +- 9 files changed, 181 insertions(+), 113 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala index 554f5c37a2..a48d421177 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala @@ -126,8 +126,8 @@ class MeetingActor( var state = new MeetingState2x(inactivityTracker, expiryTracker) /*******************************************************************/ - //object FakeTestData extends FakeTestData - //FakeTestData.createFakeUsers(liveMeeting) + object FakeTestData extends FakeTestData + FakeTestData.createFakeUsers(liveMeeting) /*******************************************************************/ def receive = { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GetGuestPolicyReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GetGuestPolicyReqMsgHdlr.scala index 91772071bd..a4e34235ef 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GetGuestPolicyReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GetGuestPolicyReqMsgHdlr.scala @@ -1,8 +1,10 @@ package org.bigbluebutton.core2.message.handlers.guests -import org.bigbluebutton.common2.msgs.GetGuestPolicyReqMsg +import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.OutMessageGateway +import org.bigbluebutton.core.models.{ GuestWaiting, GuestsWaiting, Roles, Users2x } import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting } +import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender } trait GetGuestPolicyReqMsgHdlr { this: BaseMeetingActor => @@ -11,6 +13,28 @@ trait GetGuestPolicyReqMsgHdlr { val outGW: OutMessageGateway def handleGetGuestPolicyReqMsg(msg: GetGuestPolicyReqMsg): Unit = { + val guests = GuestsWaiting.findAll(liveMeeting.guestsWaiting) + notifyModeratorsOfGuestWaiting(guests, liveMeeting.users2x, liveMeeting.props.meetingProp.intId) + val event = buildGetGuestPolicyRespMsg(liveMeeting.props.meetingProp.intId, msg.body.requestedBy, + liveMeeting.guestsWaiting.getGuestPolicy().policy) + outGW.send(event) + } + + def notifyModeratorsOfGuestWaiting(guests: Vector[GuestWaiting], users: Users2x, meetingId: String): Unit = { + val mods = Users2x.findAll(users).filter(p => p.role == Roles.MODERATOR_ROLE) + mods foreach { m => + val event = MsgBuilder.buildGuestsWaitingForApprovalEvtMsg(meetingId, m.intId, guests) + Sender.send(outGW, event) + } + } + + def buildGetGuestPolicyRespMsg(meetingId: String, userId: String, policy: String): BbbCommonEnvCoreMsg = { + val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId) + val envelope = BbbCoreEnvelope(GetGuestPolicyRespMsg.NAME, routing) + val header = BbbClientMsgHeader(GetGuestPolicyRespMsg.NAME, meetingId, userId) + val body = GetGuestPolicyRespMsgBody(policy) + val event = GetGuestPolicyRespMsg(header, body) + BbbCommonEnvCoreMsg(envelope, event) } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/testdata/FakeTestData.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/testdata/FakeTestData.scala index 0a5f0b31f8..a1fe5a21c5 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/testdata/FakeTestData.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/testdata/FakeTestData.scala @@ -9,14 +9,25 @@ import org.bigbluebutton.core.running.LiveMeeting trait FakeTestData { def createFakeUsers(liveMeeting: LiveMeeting): Unit = { - createUserVoiceAndCam(liveMeeting, Roles.MODERATOR_ROLE, false, false, CallingWith.WEBRTC, muted = false, + val mod1 = createUserVoiceAndCam(liveMeeting, Roles.MODERATOR_ROLE, false, false, CallingWith.WEBRTC, muted = false, talking = true, listenOnly = false) - createUserVoiceAndCam(liveMeeting, Roles.MODERATOR_ROLE, guest = true, authed = true, CallingWith.WEBRTC, muted = false, + Users2x.add(liveMeeting.users2x, mod1) + + val mod2 = createUserVoiceAndCam(liveMeeting, Roles.MODERATOR_ROLE, guest = false, authed = true, CallingWith.WEBRTC, muted = false, talking = false, listenOnly = false) - createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.WEBRTC, muted = false, + Users2x.add(liveMeeting.users2x, mod2) + + val guest1 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.WEBRTC, muted = false, talking = false, listenOnly = false) - createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.FLASH, muted = false, + Users2x.add(liveMeeting.users2x, guest1) + val guestWait1 = GuestWaiting(guest1.intId, guest1.name, guest1.role) + GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait1) + + val guest2 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.FLASH, muted = false, talking = false, listenOnly = false) + Users2x.add(liveMeeting.users2x, guest2) + val guestWait2 = GuestWaiting(guest2.intId, guest2.name, guest2.role) + GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait2) val vu1 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false) VoiceUsers.add(liveMeeting.voiceUsers, vu1) @@ -33,7 +44,7 @@ trait FakeTestData { } def createUserVoiceAndCam(liveMeeting: LiveMeeting, role: String, guest: Boolean, authed: Boolean, callingWith: String, - muted: Boolean, talking: Boolean, listenOnly: Boolean): Unit = { + muted: Boolean, talking: Boolean, listenOnly: Boolean): UserState = { val ruser1 = FakeUserGenerator.createFakeRegisteredUser(liveMeeting.registeredUsers, Roles.MODERATOR_ROLE, true, false) @@ -49,10 +60,10 @@ trait FakeTestData { createFakeUser(liveMeeting, ruser1) } - def createFakeUser(liveMeeting: LiveMeeting, regUser: RegisteredUser): Unit = { - val u = UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role, + def createFakeUser(liveMeeting: LiveMeeting, regUser: RegisteredUser): UserState = { + UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role, guest = regUser.guest, authed = regUser.authed, waitingForAcceptance = regUser.waitingForAcceptance, emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL) - Users2x.add(liveMeeting.users2x, u) + } } diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala index ec0131d42a..c8ade0ab93 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/GuestsMsgs.scala @@ -97,11 +97,15 @@ case class GuestPolicyChangedEvtMsgBody(policy: String, setBy: String) /** * Message from user to get the guest policy. */ -object GetGuestPolicyReqMsg { - val NAME = "GetGuestPolicyReqMsg" -} +object GetGuestPolicyReqMsg { val NAME = "GetGuestPolicyReqMsg" } case class GetGuestPolicyReqMsg(header: BbbClientMsgHeader, body: GetGuestPolicyReqMsgBody) extends StandardMsg -case class GetGuestPolicyReqMsgBody(reqeustedBy: String) - +case class GetGuestPolicyReqMsgBody(requestedBy: String) +/** + * Sent to client as response to query for guest policy. + */ +object GetGuestPolicyRespMsg { val NAME = "GetGuestPolicyRespMsg" } +case class GetGuestPolicyRespMsg(header: BbbClientMsgHeader, + body: GetGuestPolicyRespMsgBody) extends StandardMsg +case class GetGuestPolicyRespMsgBody(policy: String) diff --git a/bigbluebutton-client/src/org/bigbluebutton/core/model/users/GuestsApp.as b/bigbluebutton-client/src/org/bigbluebutton/core/model/users/GuestsApp.as index 24383e002a..647b687cf8 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/core/model/users/GuestsApp.as +++ b/bigbluebutton-client/src/org/bigbluebutton/core/model/users/GuestsApp.as @@ -10,6 +10,10 @@ package org.bigbluebutton.core.model.users private var _guests:ArrayCollection = new ArrayCollection(); + public function getGuests(): Array { + return new ArrayCollection(_guests.toArray()).toArray(); + } + public function add(user: GuestWaiting):void { _guests.addItem(user); } @@ -63,6 +67,14 @@ package org.bigbluebutton.core.model.users return -1; } + public function setGuestPolicy(value: String): void { + _guestPolicy = value; + } + + public function getGuestPolicy(): String { + return _guestPolicy; + } + public function userJoined(vu: GuestWaiting):void { add(vu); } diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/GuestWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/GuestWindow.mxml index 0410ff048c..d70fa50c12 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/views/GuestWindow.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/GuestWindow.mxml @@ -34,15 +34,19 @@ . - + . } } - private function refreshGuestView(evt:RefreshGuestEvent):void { + private function refreshGuestView(evt:NewGuestWaitingEvent):void { // do not show the guest window if the user isn't moderator or if he's waiting for acceptance if (!UsersUtil.amIModerator() || UsersUtil.amIWaitingForAcceptance()) { closeGuestWindow(); @@ -513,7 +514,7 @@ with BigBlueButton; if not, see . guestWindow.x = systemManager.screen.width - guestWindow.width - 20; guestWindow.y = 20; } - guestWindow.refreshGuestView(evt.listOfGuests); + guestWindow.refreshGuestView(); } public function removeGuestWindow(evt:BBBEvent):void { diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as index f2b8bdd935..24696a0e09 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as @@ -176,10 +176,10 @@ package org.bigbluebutton.modules.users.services case "ScreenshareRtmpBroadcastStoppedEvtMsg": handleScreenshareRtmpBroadcastStoppedEvtMsg(message); break; - case "get_guest_policy_reply": + case "GetGuestPolicyRespMsg": handleGetGuestPolicyReply(message); break; - case "guest_policy_changed": + case "GuestPolicyChangedEvtMsg": handleGuestPolicyChanged(message); break; case "guest_access_denied": @@ -755,24 +755,26 @@ package org.bigbluebutton.modules.users.services } } - - public function handleGuestPolicyChanged(msg:Object):void { - LOGGER.debug("*** handleGuestPolicyChanged " + msg.msg + " **** \n"); - var map:Object = JSON.parse(msg.msg); + var header: Object = msg.header as Object; + var body: Object = msg.body as Object; + var policy: String = body.policy as String; - var policy:BBBEvent = new BBBEvent(BBBEvent.RETRIEVE_GUEST_POLICY); - policy.payload['guestPolicy'] = map.guestPolicy; - dispatcher.dispatchEvent(policy); + var policyEvent:BBBEvent = new BBBEvent(BBBEvent.RETRIEVE_GUEST_POLICY); + policyEvent.payload['guestPolicy'] = policy; + dispatcher.dispatchEvent(policyEvent); } public function handleGetGuestPolicyReply(msg:Object):void { - LOGGER.debug("*** handleGetGuestPolicyReply " + msg.msg + " **** \n"); - var map:Object = JSON.parse(msg.msg); - - var policy:BBBEvent = new BBBEvent(BBBEvent.RETRIEVE_GUEST_POLICY); - policy.payload['guestPolicy'] = map.guestPolicy; - dispatcher.dispatchEvent(policy); + var header: Object = msg.header as Object; + var body: Object = msg.body as Object; + var policy: String = body.policy as String; + + LiveMeeting.inst().guestsWaiting.setGuestPolicy(policy); + + var policyEvent:BBBEvent = new BBBEvent(BBBEvent.RETRIEVE_GUEST_POLICY); + policyEvent.payload['guestPolicy'] = policyEvent; + dispatcher.dispatchEvent(policyEvent); } public function handleGuestAccessDenied(msg:Object):void { diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as index 9230591b84..16ca44e430 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as @@ -577,8 +577,15 @@ package org.bigbluebutton.modules.users.services public function queryForGuestPolicy():void { LOGGER.debug("queryForGuestPolicy"); + + var message:Object = { + header: {name: "GetGuestPolicyReqMsg", meetingId: UsersUtil.getInternalMeetingID(), + userId: UsersUtil.getMyUserID()}, + body: {requestedBy: UsersUtil.getMyUserID()} + }; + var _nc:ConnectionManager = BBB.initConnectionManager(); - _nc.sendMessage("participants.getGuestPolicy", + _nc.sendMessage2x( function(result:String):void { // On successful result LOGGER.debug(result); }, @@ -587,7 +594,8 @@ package org.bigbluebutton.modules.users.services logData.tags = ["apps"]; logData.message = "Error occured query guest policy."; LOGGER.info(JSON.stringify(logData)); - } + }, + JSON.stringify(message) ); } From 6be6e9191aeb063f55d08eeab5c879ca5289cddf Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Thu, 20 Jul 2017 10:09:01 -0700 Subject: [PATCH 08/10] - try to fix webcam list --- .../core/running/MeetingActor.scala | 4 +- bigbluebutton-client/.actionScriptProperties | 2 +- .../bigbluebutton/main/views/GuestWindow.mxml | 268 +++++++++--------- .../modules/users/maps/UsersMainEventMap.mxml | 2 +- .../modules/users/services/MessageSender.as | 45 ++- .../users/views/UsersWindowEventHandler.as | 11 +- .../modules/users/views/model/BBBUser2x.as | 25 +- 7 files changed, 194 insertions(+), 163 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala index a48d421177..554f5c37a2 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala @@ -126,8 +126,8 @@ class MeetingActor( var state = new MeetingState2x(inactivityTracker, expiryTracker) /*******************************************************************/ - object FakeTestData extends FakeTestData - FakeTestData.createFakeUsers(liveMeeting) + //object FakeTestData extends FakeTestData + //FakeTestData.createFakeUsers(liveMeeting) /*******************************************************************/ def receive = { diff --git a/bigbluebutton-client/.actionScriptProperties b/bigbluebutton-client/.actionScriptProperties index 06a78d9f4a..3c9a41142a 100755 --- a/bigbluebutton-client/.actionScriptProperties +++ b/bigbluebutton-client/.actionScriptProperties @@ -1,5 +1,5 @@ - + diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/views/GuestWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/main/views/GuestWindow.mxml index d70fa50c12..c2500aec3d 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/main/views/GuestWindow.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/main/views/GuestWindow.mxml @@ -1,146 +1,146 @@ - - - - - - - - - 0; - } - - public function closeWindow():void { - this.visible = false; - PopUpManager.removePopUp(this); - dispatchEvent(new CloseEvent(CloseEvent.CLOSE)); - } - - ]]> - - - - - - - - - + xmlns:fx="http://ns.adobe.com/mxml/2009" + title="{ResourceUtil.getInstance().getString('bbb.guests.title')}" showCloseButton="false" creationComplete="init()" + x="0" y="0" layout="vertical" width="320" horizontalAlign="center" + xmlns:mate="http://mate.asfusion.com/" > + + + + + + + + + 0; + } + + public function closeWindow():void { + this.visible = false; + PopUpManager.removePopUp(this); + //dispatchEvent(new CloseEvent(CloseEvent.CLOSE)); + } + + ]]> + + + + + + + + + diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/maps/UsersMainEventMap.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/users/maps/UsersMainEventMap.mxml index 0a3882a8e6..2a67382431 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/maps/UsersMainEventMap.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/maps/UsersMainEventMap.mxml @@ -194,7 +194,7 @@ with BigBlueButton; if not, see . - + diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as index 16ca44e430..c738e59f43 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageSender.as @@ -37,6 +37,8 @@ package org.bigbluebutton.modules.users.services import org.bigbluebutton.core.connection.messages.breakoutrooms.RequestBreakoutJoinURLMsg; import org.bigbluebutton.core.connection.messages.breakoutrooms.RequestBreakoutJoinURLMsgBody; import org.bigbluebutton.core.managers.ConnectionManager; + import org.bigbluebutton.core.model.LiveMeeting; + import org.bigbluebutton.core.model.users.GuestWaiting; public class MessageSender { private static const LOGGER:ILogger = getClassLogger(MessageSender); @@ -625,12 +627,17 @@ package org.bigbluebutton.modules.users.services public function responseToGuest(userId:String, response:Boolean):void { LOGGER.debug("responseToGuest - userId:[" + userId + "] response:[" + response + "]"); - var message:Object = new Object(); - message["userId"] = userId; - message["response"] = response; + var _guests: Array = new Array(); + _guests.push({guest: userId, approved: response}); + + var message:Object = { + header: {name: "GuestsWaitingApprovedMsg", meetingId: UsersUtil.getInternalMeetingID(), + userId: UsersUtil.getMyUserID()}, + body: {guests: _guests, approvedBy: UsersUtil.getMyUserID()} + }; var _nc:ConnectionManager = BBB.initConnectionManager(); - _nc.sendMessage("participants.responseToGuest", + _nc.sendMessage2x( function(result:String):void { // On successful result LOGGER.debug(result); }, @@ -640,12 +647,38 @@ package org.bigbluebutton.modules.users.services logData.message = "Error occured response guest."; LOGGER.info(JSON.stringify(logData)); }, - message + JSON.stringify(message) ); } public function responseToAllGuests(response:Boolean):void { - responseToGuest(null, response); + var _guestsWaiting: Array = LiveMeeting.inst().guestsWaiting.getGuests(); + var _guests: Array = new Array(); + + for (var i:int = 0; i < _guests.length; i++) { + var _guest: GuestWaiting = _guestsWaiting[i] as GuestWaiting; + _guests.push({guest: _guest.intId, approved: response}); + } + + var message:Object = { + header: {name: "GuestsWaitingApprovedMsg", meetingId: UsersUtil.getInternalMeetingID(), + userId: UsersUtil.getMyUserID()}, + body: {guests: _guests, approvedBy: UsersUtil.getMyUserID()} + }; + + var _nc:ConnectionManager = BBB.initConnectionManager(); + _nc.sendMessage2x( + function(result:String):void { // On successful result + LOGGER.debug(result); + }, + function(status:String):void { // status - On error occurred + var logData:Object = UsersUtil.initLogData(); + logData.tags = ["apps"]; + logData.message = "Error occured response guest."; + LOGGER.info(JSON.stringify(logData)); + }, + JSON.stringify(message) + ); } } } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindowEventHandler.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindowEventHandler.as index 48bbcf076d..2b758c46e2 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindowEventHandler.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindowEventHandler.as @@ -86,15 +86,10 @@ package org.bigbluebutton.modules.users.views users.refresh(); } - private function getWebcamStreamsForUser(userId: String): String { + private function getWebcamStreamsForUser(userId: String): Array { var streamIds: Array = LiveMeeting.inst().webcams.getStreamIdsForUser(userId); - var streams:String = ""; - for each(var stream:String in streamIds) { - streams = streams + stream + "|"; - } - //Remove last | - streams = streams.slice(0, streams.length-1); - return streams; + + return streamIds; } private function addUser(users: ArrayCollection, user: User2x):void { diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/model/BBBUser2x.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/model/BBBUser2x.as index d3de4cd743..a6884a534f 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/model/BBBUser2x.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/model/BBBUser2x.as @@ -48,7 +48,7 @@ package org.bigbluebutton.modules.users.views.model [Bindable] public function get hasStream():Boolean { - return streamNames.length > 0; + return _streamNames.length > 0; } public function set hasStream(s:Boolean):void { throw new Error("hasStream cannot be set. It is derived directly from streamName"); @@ -83,15 +83,15 @@ package org.bigbluebutton.modules.users.views.model return _viewingStream.some(function(item:*, index:int, array:Array):Boolean { return item == streamName; }); } public function isViewingAllStreams():Boolean { - return _viewingStream.length == streamNames.length; + return _viewingStream.length == _streamNames.length; } - [Bindable] public var streamNames:Array = new Array(); + private var _streamNames:Array = new Array(); [Bindable] - public function get streamName():String { + public function get streams():Array { var streams:String = ""; - for each(var stream:String in streamNames) { + for each(var stream:String in _streamNames) { streams = streams + stream + "|"; } //Remove last | @@ -100,14 +100,17 @@ package org.bigbluebutton.modules.users.views.model } private function hasThisStream(streamName:String):Boolean { - return streamNames.some(function(item:*, index:int, array:Array):Boolean { return item == streamName; }); + for each(var streamId:String in _streamNames) { + if (streamId == streamName) return true; + } + return false; } - public function set streamName(streamNames:String):void { - if(streamNames) { - var streamNamesList:Array = streamNames.split("|"); - for each(var streamName:String in streamNamesList) { - sharedWebcam(streamName); + public function set streams(streamIds:Array):void { + if (streamIds != null) { + _streamNames = streamIds; + for each(var streamId:String in _streamNames) { + sharedWebcam(streamId); } } } From ad9b122f4c8e5d88235842a1d3045f5f5826c989 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Thu, 20 Jul 2017 14:32:44 -0700 Subject: [PATCH 09/10] - try to get webcams working --- .../bigbluebutton/core/model/MediaStream.as | 16 ++ .../org/bigbluebutton/core/model/Webcams.as | 16 +- .../bigbluebutton/core/model/users/User2x.as | 57 +------ .../main/events/StartedViewingWebcamEvent.as | 38 +++++ .../modules/users/events/ViewCameraEvent.as | 6 +- .../modules/users/services/MessageReceiver.as | 6 +- .../users/views/MediaItemRenderer.mxml | 2 +- .../modules/users/views/UsersWindow.mxml | 141 ++++++++++-------- .../users/views/UsersWindowEventHandler.as | 42 +++++- .../modules/users/views/model/BBBUser2x.as | 72 ++------- .../modules/videoconf/maps/VideoEventMap.mxml | 4 +- .../videoconf/maps/VideoEventMapDelegate.as | 3 +- .../modules/videoconf/views/UserVideo.as | 27 +++- 13 files changed, 234 insertions(+), 196 deletions(-) create mode 100755 bigbluebutton-client/src/org/bigbluebutton/main/events/StartedViewingWebcamEvent.as diff --git a/bigbluebutton-client/src/org/bigbluebutton/core/model/MediaStream.as b/bigbluebutton-client/src/org/bigbluebutton/core/model/MediaStream.as index 54fdd29d97..45039624d4 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/core/model/MediaStream.as +++ b/bigbluebutton-client/src/org/bigbluebutton/core/model/MediaStream.as @@ -13,5 +13,21 @@ package org.bigbluebutton.core.model this.streamId = streamId; this.userId = userId; } + + public function addViewer(userId: String):void { + for (var i: int=0; i < viewers.length; i++) { + var viewer: String = viewers[i] as String; + if (viewer == userId) return; + } + + viewers.push(userId); + } + + public function removeViewer(userId: String):void { + var index: int = viewers.indexOf(userId); + if (index > -1 && index < viewers.length) { + viewers.removeAt(index); + } + } } } \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/core/model/Webcams.as b/bigbluebutton-client/src/org/bigbluebutton/core/model/Webcams.as index 1cbe721ed3..7477b547da 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/core/model/Webcams.as +++ b/bigbluebutton-client/src/org/bigbluebutton/core/model/Webcams.as @@ -85,7 +85,21 @@ package org.bigbluebutton.core.model return tempArray; } - public function getStreamIdsViewingForUser(userId: String): Array { + public function startedViewingStream(userId: String, streamId: String): void { + var _stream: MediaStream = getStream(streamId); + if (_stream != null) { + _stream.addViewer(userId); + } + } + + public function stoppedViewingStream(userId: String, streamId: String): void { + var _stream: MediaStream = getStream(streamId); + if (_stream != null) { + _stream.removeViewer(userId); + } + } + + public function getStreamIdsIAmViewingForUser(userId: String): Array { var tempArray: Array = new Array(); for (var i:int = 0; i < _webcams.length; i++) { diff --git a/bigbluebutton-client/src/org/bigbluebutton/core/model/users/User2x.as b/bigbluebutton-client/src/org/bigbluebutton/core/model/users/User2x.as index c77edc3997..86d7ec7673 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/core/model/users/User2x.as +++ b/bigbluebutton-client/src/org/bigbluebutton/core/model/users/User2x.as @@ -18,62 +18,7 @@ package org.bigbluebutton.core.model.users // Flag to tell that user is in the process of leaving the meeting. public var isLeavingFlag:Boolean = false; - - [Bindable] private var _viewingStream:Array = new Array(); - - [Bindable] - public function get viewingStream():Array { - return _viewingStream; - } - public function set viewingStream(v:Array):void { - throw new Error("Please use the helpers addViewingStream or removeViewingStream to handle viewingStream"); - } - public function addViewingStream(streamName:String):Boolean { - if (isViewingStream(streamName)) { - return false; - } - - _viewingStream.push(streamName); - return true; - } - public function removeViewingStream(streamName:String):Boolean { - if (!isViewingStream(streamName)) { - return false; - } - - _viewingStream = _viewingStream.filter(function(item:*, index:int, array:Array):Boolean { return item != streamName; }); - return true; - } - private function isViewingStream(streamName:String):Boolean { - return _viewingStream.some(function(item:*, index:int, array:Array):Boolean { return item == streamName; }); - } - public function isViewingAllStreams():Boolean { - return _viewingStream.length == streamNames.length; - } - - [Bindable] public var streamNames:Array = new Array(); - - [Bindable] - public function get streamName():String { - var streams:String = ""; - for each(var stream:String in streamNames) { - streams = streams + stream + "|"; - } - //Remove last | - streams = streams.slice(0, streams.length-1); - return streams; - } - - private function hasThisStream(streamName:String):Boolean { - return streamNames.some(function(item:*, index:int, array:Array):Boolean { return item == streamName; }); - } - - public function set streamName(streamNames:String):void { - if(streamNames) { - var streamNamesList:Array = streamNames.split("|"); - } - } - + public static function copy(source: User2x):User2x { var dest: User2x = new User2x(); dest.intId = source.intId; diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/events/StartedViewingWebcamEvent.as b/bigbluebutton-client/src/org/bigbluebutton/main/events/StartedViewingWebcamEvent.as new file mode 100755 index 0000000000..e245fa3f27 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/main/events/StartedViewingWebcamEvent.as @@ -0,0 +1,38 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 3.0 of the License, or (at your option) any later + * version. + * + * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see . + * + */ +package org.bigbluebutton.main.events +{ + import flash.events.Event; + + public class StartedViewingWebcamEvent extends Event + { + public static const STARTED_VIEWING_WEBCAM:String = "started viewing webcam event"; + + // The userID of the webcam being viewed. + public var webcamUserID:String; + + // The streamName of the user + public var streamName:String; + + public function StartedViewingWebcamEvent(bubbles:Boolean=true, cancelable:Boolean=false) + { + super(STARTED_VIEWING_WEBCAM, bubbles, cancelable); + } + } +} diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/events/ViewCameraEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/events/ViewCameraEvent.as index a1be60df85..53408e2551 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/events/ViewCameraEvent.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/events/ViewCameraEvent.as @@ -24,16 +24,12 @@ package org.bigbluebutton.modules.users.events { public static const VIEW_CAMERA_EVENT:String = "VIEW_CAMERA_EVENT"; - public var stream:String; - public var viewedName:String; public var userID:String; - public function ViewCameraEvent(userID:String, stream:String, viewedName:String) + public function ViewCameraEvent(userID:String) { super(VIEW_CAMERA_EVENT,true); this.userID = userID; - this.stream = stream; - this.viewedName = viewedName; } } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as index 5c6a7664f6..01504a2a35 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as @@ -654,7 +654,6 @@ package org.bigbluebutton.modules.users.services var webUser: User2x = UsersUtil.getUser(userId); if (webUser != null) { - webUser.streamNames.push(streamId); sendStreamStartedEvent(userId, webUser.name, streamId); } @@ -675,6 +674,8 @@ package org.bigbluebutton.modules.users.services logData.user.webcamStream = stream; LOGGER.info(JSON.stringify(logData)); + LiveMeeting.inst().webcams.remove(stream); + sendStreamStoppedEvent(userId, stream); } @@ -683,9 +684,6 @@ package org.bigbluebutton.modules.users.services dispatcher.dispatchEvent(new StreamStoppedEvent(userId, streamId)); } - - - private function handleBreakoutRoomsList(msg:Object):void{ for each(var room : Object in msg.body.rooms) diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/MediaItemRenderer.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/MediaItemRenderer.mxml index 1134db5209..f8654f226b 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/MediaItemRenderer.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/MediaItemRenderer.mxml @@ -142,7 +142,7 @@ } private function viewCamera():void { - dispatchEvent(new ViewCameraEvent(data.userId, data.streamName, data.name)); + dispatchEvent(new ViewCameraEvent(data.userId)); } private function kickUser():void{ diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml index 169eb5f0e5..8b07cbfa7b 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/views/UsersWindow.mxml @@ -52,69 +52,77 @@ + + + + - + . - + @@ -96,7 +96,7 @@ with BigBlueButton; if not, see . - + diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as index 618a588fda..c898840882 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as @@ -114,11 +114,10 @@ package org.bigbluebutton.modules.videoconf.maps _graphics.addStaticComponent(component); } - public function viewCamera(userID:String, stream:String, name:String, mock:Boolean = false):void { + public function viewCamera(userID:String):void { LOGGER.debug("VideoEventMapDelegate:: [{0}] viewCamera. ready = [{1}]", [me, _ready]); if (!_ready) return; - LOGGER.debug("VideoEventMapDelegate:: [{0}] Viewing [{1} stream [{2}]", [me, userID, stream]); if (! UsersUtil.isMe(userID)) { openViewWindowFor(userID); } diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/UserVideo.as b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/UserVideo.as index e39339c5a1..535fe0e71b 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/UserVideo.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/UserVideo.as @@ -11,13 +11,15 @@ package org.bigbluebutton.modules.videoconf.views import org.as3commons.logging.api.ILogger; import org.as3commons.logging.api.getClassLogger; import org.bigbluebutton.core.BBB; - import org.bigbluebutton.core.model.VideoProfile; import org.bigbluebutton.core.UsersUtil; + import org.bigbluebutton.core.model.LiveMeeting; + import org.bigbluebutton.core.model.VideoProfile; import org.bigbluebutton.main.events.BBBEvent; + import org.bigbluebutton.main.events.StartedViewingWebcamEvent; import org.bigbluebutton.main.events.StoppedViewingWebcamEvent; import org.bigbluebutton.main.views.VideoWithWarnings; import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent; - import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent; + import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent; public class UserVideo extends UserGraphic { private static const LOGGER:ILogger = getClassLogger(UserVideo); @@ -125,13 +127,29 @@ package org.bigbluebutton.modules.videoconf.views } private function stopViewing():void { + // Store that I stopped viewing this streamId; + var myUserId: String = UsersUtil.getMyUserID(); + LiveMeeting.inst().webcams.stoppedViewingStream(myUserId, _streamName); + var stopEvent:StoppedViewingWebcamEvent = new StoppedViewingWebcamEvent(); stopEvent.webcamUserID = user.intId; stopEvent.streamName = _streamName; _dispatcher.dispatchEvent(stopEvent); - user.removeViewingStream(_streamName); + } + private function startedViewing():void { + // Store that I started viewing this streamId; + var myUserId: String = UsersUtil.getMyUserID(); + LiveMeeting.inst().webcams.startedViewingStream(myUserId, _streamName); + + var startEvent:StartedViewingWebcamEvent = new StartedViewingWebcamEvent(); + startEvent.webcamUserID = user.intId; + startEvent.streamName = _streamName; + _dispatcher.dispatchEvent(startEvent); + + } + private function stopPublishing():void { var e:StopBroadcastEvent = new StopBroadcastEvent(); e.stream = _streamName; @@ -183,7 +201,8 @@ package org.bigbluebutton.modules.videoconf.views _ns.play(streamName); - user.addViewingStream(streamName); + startedViewing(); + invalidateDisplayList(); } From 905f1a8e7128fd8514f6ca1a30dd61cdd900e45d Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Thu, 20 Jul 2017 17:33:49 -0700 Subject: [PATCH 10/10] - clean up --- .../handlers/guests/GetGuestPolicyReqMsgHdlr.scala | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GetGuestPolicyReqMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GetGuestPolicyReqMsgHdlr.scala index a4e34235ef..3e871766ec 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GetGuestPolicyReqMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GetGuestPolicyReqMsgHdlr.scala @@ -13,22 +13,11 @@ trait GetGuestPolicyReqMsgHdlr { val outGW: OutMessageGateway def handleGetGuestPolicyReqMsg(msg: GetGuestPolicyReqMsg): Unit = { - val guests = GuestsWaiting.findAll(liveMeeting.guestsWaiting) - notifyModeratorsOfGuestWaiting(guests, liveMeeting.users2x, liveMeeting.props.meetingProp.intId) - val event = buildGetGuestPolicyRespMsg(liveMeeting.props.meetingProp.intId, msg.body.requestedBy, liveMeeting.guestsWaiting.getGuestPolicy().policy) outGW.send(event) } - def notifyModeratorsOfGuestWaiting(guests: Vector[GuestWaiting], users: Users2x, meetingId: String): Unit = { - val mods = Users2x.findAll(users).filter(p => p.role == Roles.MODERATOR_ROLE) - mods foreach { m => - val event = MsgBuilder.buildGuestsWaitingForApprovalEvtMsg(meetingId, m.intId, guests) - Sender.send(outGW, event) - } - } - def buildGetGuestPolicyRespMsg(meetingId: String, userId: String, policy: String): BbbCommonEnvCoreMsg = { val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId) val envelope = BbbCoreEnvelope(GetGuestPolicyRespMsg.NAME, routing)