Merge branch 'v2.6.x-release' of https://github.com/bigbluebutton/bigbluebutton into tldraw-recording
This commit is contained in:
commit
c2db91b5f9
28
.travis.yml
28
.travis.yml
@ -1,28 +0,0 @@
|
||||
dist: focal
|
||||
language: node_js
|
||||
node_js: 16
|
||||
|
||||
env:
|
||||
- JOB_TYPE=linter
|
||||
- JOB_TYPE=acceptance_tests
|
||||
global:
|
||||
- BBB_SERVER_URL=http://localhost/bigbluebutton/api
|
||||
|
||||
jobs:
|
||||
allow_failures:
|
||||
- env: JOB_TYPE=linter
|
||||
include:
|
||||
- stage: "Linter"
|
||||
name: "ESLint"
|
||||
env: JOB_TYPE=linter
|
||||
- stage: "Tests"
|
||||
name: "Acceptance Tests"
|
||||
env: JOB_TYPE=acceptance_tests
|
||||
|
||||
script:
|
||||
- travis_wait 30 bash ./build_script.sh $JOB_TYPE
|
||||
|
||||
after_script:
|
||||
- docker stop $docker
|
||||
- docker rm $docker
|
||||
- docker rmi b2
|
@ -75,6 +75,12 @@ object LockSettingsUtil {
|
||||
}
|
||||
}
|
||||
|
||||
def isMicrophoneSharingLocked(user: UserState, liveMeeting: LiveMeeting): Boolean = {
|
||||
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||
|
||||
user.role == Roles.VIEWER_ROLE && user.locked && permissions.disableMic
|
||||
}
|
||||
|
||||
def isCameraBroadcastLocked(user: UserState, liveMeeting: LiveMeeting): Boolean = {
|
||||
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
|
||||
|
||||
|
@ -70,11 +70,6 @@ trait SystemConfiguration {
|
||||
|
||||
lazy val fromBbbWebRedisChannel = Try(config.getString("redis.fromBbbWebRedisChannel")).getOrElse("from-bbb-web-redis-channel")
|
||||
|
||||
lazy val toAkkaTranscodeRedisChannel = Try(config.getString("redis.toAkkaTranscodeRedisChannel")).getOrElse("bigbluebutton:to-bbb-transcode:system")
|
||||
lazy val fromAkkaTranscodeRedisChannel = Try(config.getString("redis.fromAkkaTranscodeRedisChannel")).getOrElse("bigbluebutton:from-bbb-transcode:system")
|
||||
lazy val toAkkaTranscodeJsonChannel = Try(config.getString("eventBus.toAkkaTranscodeJsonChannel")).getOrElse("to-akka-transcode-json-channel")
|
||||
lazy val fromAkkaTranscodeJsonChannel = Try(config.getString("eventBus.fromAkkaTranscodeJsonChannel")).getOrElse("from-akka-transcode-json-channel")
|
||||
|
||||
lazy val analyticsIncludeChat = Try(config.getBoolean("analytics.includeChat")).getOrElse(true)
|
||||
|
||||
// Grab the "interface" parameter from the http config
|
||||
|
@ -187,9 +187,6 @@ class BigBlueButtonActor(
|
||||
val disconnectEvnt = MsgBuilder.buildDisconnectAllClientsSysMsg(msg.meetingId, "meeting-destroyed")
|
||||
m2.outMsgRouter.send(disconnectEvnt)
|
||||
|
||||
val stopTranscodersCmd = MsgBuilder.buildStopMeetingTranscodersSysCmdMsg(msg.meetingId)
|
||||
m2.outMsgRouter.send(stopTranscodersCmd)
|
||||
|
||||
log.info("Destroyed meetingId={}", msg.meetingId)
|
||||
val destroyedEvent = MsgBuilder.buildMeetingDestroyedEvtMsg(msg.meetingId)
|
||||
m2.outMsgRouter.send(destroyedEvent)
|
||||
|
@ -25,8 +25,7 @@ trait GetScreenBroadcastPermissionReqMsgHdlr {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
val reason = "No permission to share the screen."
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
||||
} else if (!user.userLeftFlag.left
|
||||
&& liveMeeting.props.meetingProp.intId == msg.body.meetingId
|
||||
} else if (liveMeeting.props.meetingProp.intId == msg.body.meetingId
|
||||
&& liveMeeting.props.voiceProp.voiceConf == msg.body.voiceConf) {
|
||||
allowed = true
|
||||
}
|
||||
|
@ -17,8 +17,7 @@ trait GetScreenSubscribePermissionReqMsgHdlr {
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
|
||||
} yield {
|
||||
if (!user.userLeftFlag.left
|
||||
&& liveMeeting.props.meetingProp.intId == msg.body.meetingId
|
||||
if (liveMeeting.props.meetingProp.intId == msg.body.meetingId
|
||||
&& liveMeeting.props.voiceProp.voiceConf == msg.body.voiceConf
|
||||
&& ScreenshareModel.getRTMPBroadcastingUrl(liveMeeting.screenshareModel) == msg.body.streamId) {
|
||||
allowed = true
|
||||
|
@ -1,35 +1,51 @@
|
||||
package org.bigbluebutton.core2.message.handlers
|
||||
package org.bigbluebutton.core.apps.voice
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
import org.bigbluebutton.core.models.Users2x
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
||||
|
||||
trait GetGlobalAudioPermissionReqMsgHdlr {
|
||||
this: MeetingActor =>
|
||||
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleGetGlobalAudioPermissionReqMsg(msg: GetGlobalAudioPermissionReqMsg) {
|
||||
var allowed = false
|
||||
def handleGetGlobalAudioPermissionReqMsg(msg: GetGlobalAudioPermissionReqMsg): Unit = {
|
||||
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
|
||||
} yield {
|
||||
if (!user.userLeftFlag.left
|
||||
&& liveMeeting.props.meetingProp.intId == msg.body.meetingId
|
||||
&& liveMeeting.props.voiceProp.voiceConf == msg.body.voiceConf) {
|
||||
allowed = true
|
||||
}
|
||||
def broadcastEvent(
|
||||
meetingId: String,
|
||||
voiceConf: String,
|
||||
userId: String,
|
||||
sfuSessionId: String,
|
||||
allowed: Boolean
|
||||
): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
|
||||
val envelope = BbbCoreEnvelope(GetGlobalAudioPermissionRespMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(GetGlobalAudioPermissionRespMsg.NAME, meetingId, userId)
|
||||
val body = GetGlobalAudioPermissionRespMsgBody(
|
||||
meetingId,
|
||||
voiceConf,
|
||||
userId,
|
||||
sfuSessionId,
|
||||
allowed
|
||||
)
|
||||
val event = GetGlobalAudioPermissionRespMsg(header, body)
|
||||
val eventMsg = BbbCommonEnvCoreMsg(envelope, event)
|
||||
|
||||
outGW.send(eventMsg)
|
||||
}
|
||||
|
||||
val event = MsgBuilder.buildGetGlobalAudioPermissionRespMsg(
|
||||
val allowed = VoiceHdlrHelpers.isGlobalAudioSubscribeAllowed(
|
||||
liveMeeting,
|
||||
msg.body.meetingId,
|
||||
msg.body.userId,
|
||||
msg.body.voiceConf
|
||||
)
|
||||
|
||||
broadcastEvent(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
liveMeeting.props.voiceProp.voiceConf,
|
||||
msg.body.userId,
|
||||
msg.body.sfuSessionId,
|
||||
allowed
|
||||
)
|
||||
outGW.send(event)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
package org.bigbluebutton.core.apps.voice
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
||||
|
||||
trait GetMicrophonePermissionReqMsgHdlr {
|
||||
this: MeetingActor =>
|
||||
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleGetMicrophonePermissionReqMsg(msg: GetMicrophonePermissionReqMsg): Unit = {
|
||||
|
||||
def broadcastEvent(
|
||||
meetingId: String,
|
||||
voiceConf: String,
|
||||
userId: String,
|
||||
sfuSessionId: String,
|
||||
allowed: Boolean
|
||||
): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
|
||||
val envelope = BbbCoreEnvelope(GetMicrophonePermissionRespMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(GetMicrophonePermissionRespMsg.NAME, meetingId, userId)
|
||||
val body = GetMicrophonePermissionRespMsgBody(
|
||||
meetingId,
|
||||
voiceConf,
|
||||
userId,
|
||||
sfuSessionId,
|
||||
allowed
|
||||
)
|
||||
val event = GetMicrophonePermissionRespMsg(header, body)
|
||||
val eventMsg = BbbCommonEnvCoreMsg(envelope, event)
|
||||
|
||||
outGW.send(eventMsg)
|
||||
}
|
||||
|
||||
val allowed = VoiceHdlrHelpers.isMicrophoneSharingAllowed(
|
||||
liveMeeting,
|
||||
msg.body.meetingId,
|
||||
msg.body.userId,
|
||||
msg.body.voiceConf,
|
||||
msg.body.callerIdNum
|
||||
)
|
||||
|
||||
broadcastEvent(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
liveMeeting.props.voiceProp.voiceConf,
|
||||
msg.body.userId,
|
||||
msg.body.sfuSessionId,
|
||||
allowed
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package org.bigbluebutton.core.apps.voice
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.models.{ Users2x, VoiceUsers }
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting }
|
||||
import org.bigbluebutton.LockSettingsUtil
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
|
||||
object VoiceHdlrHelpers extends SystemConfiguration {
|
||||
def isGlobalAudioSubscribeAllowed(
|
||||
liveMeeting: LiveMeeting,
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
voiceConf: String
|
||||
): Boolean = {
|
||||
Users2x.findWithIntId(liveMeeting.users2x, userId) match {
|
||||
case Some(user) => (
|
||||
applyPermissionCheck &&
|
||||
liveMeeting.props.meetingProp.intId == meetingId &&
|
||||
liveMeeting.props.voiceProp.voiceConf == voiceConf
|
||||
)
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
def isMicrophoneSharingAllowed(
|
||||
liveMeeting: LiveMeeting,
|
||||
meetingId: String,
|
||||
userId: String,
|
||||
voiceConf: String,
|
||||
callerIdNum: String
|
||||
): Boolean = {
|
||||
Users2x.findWithIntId(liveMeeting.users2x, userId) match {
|
||||
case Some(user) => {
|
||||
val microphoneSharingLocked = LockSettingsUtil.isMicrophoneSharingLocked(
|
||||
user,
|
||||
liveMeeting
|
||||
)
|
||||
val isCallerBanned = VoiceUsers.isCallerBanned(
|
||||
callerIdNum,
|
||||
liveMeeting.voiceUsers
|
||||
)
|
||||
|
||||
(applyPermissionCheck &&
|
||||
!isCallerBanned &&
|
||||
!microphoneSharingLocked &&
|
||||
liveMeeting.props.meetingProp.intId == meetingId &&
|
||||
liveMeeting.props.voiceProp.voiceConf == voiceConf)
|
||||
}
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,6 @@ object CameraHdlrHelpers extends SystemConfiguration with RightsManagementTrait
|
||||
(applyPermissionCheck &&
|
||||
!camBroadcastLocked &&
|
||||
!camCapReached &&
|
||||
!user.userLeftFlag.left &&
|
||||
streamId.startsWith(user.intId) &&
|
||||
liveMeeting.props.meetingProp.intId == meetingId)
|
||||
}
|
||||
@ -43,7 +42,6 @@ object CameraHdlrHelpers extends SystemConfiguration with RightsManagementTrait
|
||||
|
||||
(applyPermissionCheck &&
|
||||
!camSubscribeLocked &&
|
||||
!user.userLeftFlag.left &&
|
||||
liveMeeting.props.meetingProp.intId == meetingId)
|
||||
}
|
||||
case _ => false
|
||||
|
@ -215,6 +215,8 @@ class ReceivedJsonMsgHandlerActor(
|
||||
routeVoiceMsg[VoiceConfCallStateEvtMsg](envelope, jsonNode)
|
||||
case GetGlobalAudioPermissionReqMsg.NAME =>
|
||||
routeGenericMsg[GetGlobalAudioPermissionReqMsg](envelope, jsonNode)
|
||||
case GetMicrophonePermissionReqMsg.NAME =>
|
||||
routeGenericMsg[GetMicrophonePermissionReqMsg](envelope, jsonNode)
|
||||
|
||||
// Breakout rooms
|
||||
case BreakoutRoomsListMsg.NAME =>
|
||||
|
@ -80,6 +80,7 @@ class MeetingActor(
|
||||
with MuteMeetingCmdMsgHdlr
|
||||
with IsMeetingMutedReqMsgHdlr
|
||||
with GetGlobalAudioPermissionReqMsgHdlr
|
||||
with GetMicrophonePermissionReqMsgHdlr
|
||||
with GetScreenBroadcastPermissionReqMsgHdlr
|
||||
with GetScreenSubscribePermissionReqMsgHdlr
|
||||
|
||||
@ -467,6 +468,8 @@ class MeetingActor(
|
||||
handleUserStatusVoiceConfEvtMsg(m)
|
||||
case m: GetGlobalAudioPermissionReqMsg =>
|
||||
handleGetGlobalAudioPermissionReqMsg(m)
|
||||
case m: GetMicrophonePermissionReqMsg =>
|
||||
handleGetMicrophonePermissionReqMsg(m)
|
||||
|
||||
// Layout
|
||||
case m: GetCurrentLayoutReqMsg => handleGetCurrentLayoutReqMsg(m)
|
||||
|
@ -347,16 +347,6 @@ object MsgBuilder {
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
def buildStopMeetingTranscodersSysCmdMsg(meetingId: String): BbbCommonEnvCoreMsg = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(StopMeetingTranscodersSysCmdMsg.NAME, routing)
|
||||
val body = StopMeetingTranscodersSysCmdMsgBody()
|
||||
val header = BbbCoreHeaderWithMeetingId(StopMeetingTranscodersSysCmdMsg.NAME, meetingId)
|
||||
val event = StopMeetingTranscodersSysCmdMsg(header, body)
|
||||
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
def buildRecordingChapterBreakSysMsg(meetingId: String, timestamp: Long): BbbCommonEnvCoreMsg = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(RecordingChapterBreakSysMsg.NAME, routing)
|
||||
@ -461,23 +451,6 @@ object MsgBuilder {
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
def buildGetGlobalAudioPermissionRespMsg(
|
||||
meetingId: String,
|
||||
voiceConf: String,
|
||||
userId: String,
|
||||
sfuSessionId: String,
|
||||
allowed: Boolean
|
||||
): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
|
||||
val envelope = BbbCoreEnvelope(GetGlobalAudioPermissionRespMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(GetGlobalAudioPermissionRespMsg.NAME, meetingId, userId)
|
||||
|
||||
val body = GetGlobalAudioPermissionRespMsgBody(meetingId, voiceConf, userId, sfuSessionId, allowed)
|
||||
val event = GetGlobalAudioPermissionRespMsg(header, body)
|
||||
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
def buildMeetingTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long, timeUpdatedInMinutes: Int = 0): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
|
||||
val envelope = BbbCoreEnvelope(MeetingTimeRemainingUpdateEvtMsg.NAME, routing)
|
||||
|
@ -46,8 +46,8 @@ object Dependencies {
|
||||
|
||||
val apacheLang = "org.apache.commons" % "commons-lang3" % Versions.lang
|
||||
|
||||
val bbbCommons = "org.bigbluebutton" % "bbb-common-message_2.13" % Versions.bbbCommons excludeAll (
|
||||
ExclusionRule(organization = "org.red5"))
|
||||
val bbbCommons = "org.bigbluebutton" % "bbb-common-message_2.13" % Versions.bbbCommons
|
||||
|
||||
val bbbFseslClient = "org.bigbluebutton" % "bbb-fsesl-client" % Versions.bbbFsesl
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,6 @@ public class MessageSender extends RedisAwareCommunicator {
|
||||
connectionPool = ConnectionPoolSupport.createGenericObjectPool(() -> redisClient.connectPubSub(),
|
||||
createPoolingConfig());
|
||||
|
||||
log.info("Redis org.bigbluebutton.red5.pubsub.message publisher starting!");
|
||||
try {
|
||||
sendMessage = true;
|
||||
|
||||
@ -84,7 +83,7 @@ public class MessageSender extends RedisAwareCommunicator {
|
||||
RedisAsyncCommands<String, String> async = connection.async();
|
||||
RedisFuture<Long> future = async.publish(channel, message);
|
||||
} catch (Exception e) {
|
||||
log.warn("Cannot publish the org.bigbluebutton.red5.pubsub.message to redis", e);
|
||||
log.warn("Cannot publish the message to redis", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -49,8 +49,6 @@ case class UsersProp(
|
||||
|
||||
case class MetadataProp(metadata: collection.immutable.Map[String, String])
|
||||
|
||||
case class ScreenshareProps(screenshareConf: String, red5ScreenshareIp: String, red5ScreenshareApp: String)
|
||||
|
||||
case class LockSettingsProps(
|
||||
disableCam: Boolean,
|
||||
disableMic: Boolean,
|
||||
|
@ -1,43 +0,0 @@
|
||||
package org.bigbluebutton.common2.msgs
|
||||
|
||||
/* In Messages */
|
||||
object StartProbingSysReqMsg { val NAME = "StartProbingSysReqMsg" }
|
||||
case class StartProbingSysReqMsg(header: BbbCoreHeaderWithMeetingId, body: StartProbingSysReqMsgBody) extends BbbCoreMsg
|
||||
case class StartProbingSysReqMsgBody(transcoderId: String, params: Map[String, String])
|
||||
|
||||
object StartTranscoderSysReqMsg { val NAME = "StartTranscoderSysReqMsg" }
|
||||
case class StartTranscoderSysReqMsg(header: BbbCoreHeaderWithMeetingId, body: StartTranscoderSysReqMsgBody) extends BbbCoreMsg
|
||||
case class StartTranscoderSysReqMsgBody(transcoderId: String, params: Map[String, String])
|
||||
|
||||
object StopTranscoderSysReqMsg { val NAME = "StopTranscoderSysReqMsg" }
|
||||
case class StopTranscoderSysReqMsg(header: BbbCoreHeaderWithMeetingId, body: StopTranscoderSysReqMsgBody) extends BbbCoreMsg
|
||||
case class StopTranscoderSysReqMsgBody(transcoderId: String)
|
||||
|
||||
object UpdateTranscoderSysReqMsg { val NAME = "UpdateTranscoderSysReqMsg" }
|
||||
case class UpdateTranscoderSysReqMsg(header: BbbCoreHeaderWithMeetingId, body: UpdateTranscoderSysReqMsgBody) extends BbbCoreMsg
|
||||
case class UpdateTranscoderSysReqMsgBody(transcoderId: String, params: Map[String, String])
|
||||
|
||||
object TranscoderStatusUpdateSysCmdMsg { val NAME = "TranscoderStatusUpdateSysCmdMsg" }
|
||||
case class TranscoderStatusUpdateSysCmdMsg(header: BbbCoreHeaderWithMeetingId, body: TranscoderStatusUpdateSysCmdMsgBody) extends BbbCoreMsg
|
||||
case class TranscoderStatusUpdateSysCmdMsgBody(transcoderId: String, params: Map[String, String])
|
||||
|
||||
object StopMeetingTranscodersSysCmdMsg { val NAME = "StopMeetingTranscodersSysCmdMsg" }
|
||||
case class StopMeetingTranscodersSysCmdMsg(header: BbbCoreHeaderWithMeetingId, body: StopMeetingTranscodersSysCmdMsgBody) extends BbbCoreMsg
|
||||
case class StopMeetingTranscodersSysCmdMsgBody()
|
||||
|
||||
/* Out Messages */
|
||||
object StartProbingSysRespMsg { val NAME = "StartProbingSysRespMsg" }
|
||||
case class StartProbingSysRespMsg(header: BbbCoreHeaderWithMeetingId, body: StartProbingSysRespMsgBody) extends BbbCoreMsg
|
||||
case class StartProbingSysRespMsgBody(transcoderId: String, params: Map[String, String])
|
||||
|
||||
object StartTranscoderSysRespMsg { val NAME = "StartTranscoderSysRespMsg" }
|
||||
case class StartTranscoderSysRespMsg(header: BbbCoreHeaderWithMeetingId, body: StartTranscoderSysRespMsgBody) extends BbbCoreMsg
|
||||
case class StartTranscoderSysRespMsgBody(transcoderId: String, params: Map[String, String])
|
||||
|
||||
object StopTranscoderSysRespMsg { val NAME = "StopTranscoderSysRespMsg" }
|
||||
case class StopTranscoderSysRespMsg(header: BbbCoreHeaderWithMeetingId, body: StopTranscoderSysRespMsgBody) extends BbbCoreMsg
|
||||
case class StopTranscoderSysRespMsgBody(transcoderId: String)
|
||||
|
||||
object UpdateTranscoderSysRespMsg { val NAME = "UpdateTranscoderSysRespMsg" }
|
||||
case class UpdateTranscoderSysRespMsg(header: BbbCoreHeaderWithMeetingId, body: UpdateTranscoderSysRespMsgBody) extends BbbCoreMsg
|
||||
case class UpdateTranscoderSysRespMsgBody(transcoderId: String, params: Map[String, String])
|
@ -310,7 +310,7 @@ case class UserJoinMeetingAfterReconnectReqMsg(header: BbbClientMsgHeader, body:
|
||||
case class UserJoinMeetingAfterReconnectReqMsgBody(userId: String, authToken: String, clientType: String)
|
||||
|
||||
/**
|
||||
* Sent from bbb-apps when user disconnects from Red5.
|
||||
* Sent from client to bbb-akka to notify that a user is leaving
|
||||
*/
|
||||
object UserLeaveReqMsg { val NAME = "UserLeaveReqMsg" }
|
||||
case class UserLeaveReqMsg(header: BbbClientMsgHeader, body: UserLeaveReqMsgBody) extends StandardMsg
|
||||
|
@ -561,3 +561,40 @@ case class GetGlobalAudioPermissionRespMsgBody(
|
||||
sfuSessionId: String,
|
||||
allowed: Boolean
|
||||
)
|
||||
|
||||
/* Sent by bbb-webrtc-sfu to ask permission for a new microphone/full audio
|
||||
* connection
|
||||
* - callerIdNum: the session's callerId as assembled by the requester
|
||||
* - sfuSessionId: the UUID for this request's session in bbb-webrtc-sfu.
|
||||
* Used for response matching.
|
||||
*/
|
||||
object GetMicrophonePermissionReqMsg { val NAME = "GetMicrophonePermissionReqMsg" }
|
||||
case class GetMicrophonePermissionReqMsg(
|
||||
header: BbbClientMsgHeader,
|
||||
body: GetMicrophonePermissionReqMsgBody
|
||||
) extends StandardMsg
|
||||
case class GetMicrophonePermissionReqMsgBody(
|
||||
meetingId: String,
|
||||
voiceConf: String,
|
||||
userId: String,
|
||||
callerIdNum: String,
|
||||
sfuSessionId: String
|
||||
)
|
||||
|
||||
/* Sent to bbb-webrtc-sfu as a response to GetMicrophonePermissionReqMsg
|
||||
* - sfuSessionId: the UUID for this request's session in bbb-webrtc-sfu.
|
||||
* Used for response matching.
|
||||
* - allowed: whether session creation should be allowed.
|
||||
*/
|
||||
object GetMicrophonePermissionRespMsg { val NAME = "GetMicrophonePermissionRespMsg" }
|
||||
case class GetMicrophonePermissionRespMsg(
|
||||
header: BbbClientMsgHeader,
|
||||
body: GetMicrophonePermissionRespMsgBody
|
||||
) extends StandardMsg
|
||||
case class GetMicrophonePermissionRespMsgBody(
|
||||
meetingId: String,
|
||||
voiceConf: String,
|
||||
userId: String,
|
||||
sfuSessionId: String,
|
||||
allowed: Boolean
|
||||
)
|
||||
|
@ -1 +1 @@
|
||||
git clone --branch v2.9.0-alpha.0 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
|
||||
git clone --branch v2.9.0-alpha.3 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
|
||||
|
@ -421,7 +421,15 @@ start_bigbluebutton () {
|
||||
TOMCAT_SERVICE=$TOMCAT_USER
|
||||
fi
|
||||
|
||||
systemctl start $TOMCAT_SERVICE nginx freeswitch $REDIS_SERVICE bbb-apps-akka bbb-fsesl-akka bbb-rap-resque-worker bbb-rap-starter.service bbb-rap-caption-inbox.service $HTML5 $WEBHOOKS $ETHERPAD $PADS $BBB_WEB $BBB_LTI
|
||||
systemctl start $TOMCAT_SERVICE || {
|
||||
echo
|
||||
echo "# Warning: $TOMCAT_SERVICE could not be started. Please, check BBB-LTI or BBB-Demo."
|
||||
echo "# Run the command:"
|
||||
echo "# sudo journalctl -u $TOMCAT_SERVICE"
|
||||
echo "# To better understand the ERROR"
|
||||
}
|
||||
|
||||
systemctl start nginx freeswitch $REDIS_SERVICE bbb-apps-akka bbb-fsesl-akka bbb-rap-resque-worker bbb-rap-starter.service bbb-rap-caption-inbox.service $HTML5 $WEBHOOKS $ETHERPAD $PADS $BBB_WEB $BBB_LTI
|
||||
|
||||
if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then
|
||||
systemctl start mongod
|
||||
@ -1134,10 +1142,19 @@ check_state() {
|
||||
fi
|
||||
|
||||
if bbb-conf --status | grep -q inactive; then
|
||||
echo "# Error: Detected some processes have not started correctly"
|
||||
echo "#"
|
||||
echo "# $(bbb-conf --status | grep inactive)"
|
||||
echo "#"
|
||||
|
||||
if bbb-conf --status | grep -q tomcat9; then
|
||||
echo "# Warning: $TOMCAT_SERVICE is not started correctly"
|
||||
echo "#"
|
||||
echo "# $(bbb-conf --status | grep inactive | grep $TOMCAT_SERVICE)"
|
||||
echo "#"
|
||||
fi
|
||||
if bbb-conf --status | grep inactive | grep -vq tomcat9; then
|
||||
echo "# Error: Detected some processes have not started correctly"
|
||||
echo "#"
|
||||
echo "# $(bbb-conf --status | grep inactive | grep -v $TOMCAT_SERVICE)"
|
||||
echo "#"
|
||||
fi
|
||||
fi
|
||||
|
||||
if systemctl status freeswitch | grep -q SETSCHEDULER; then
|
||||
|
@ -134,7 +134,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<script src="compatibility/tflite-simd.js?v=VERSION" language="javascript"></script>
|
||||
<script src="compatibility/tflite.js?v=VERSION" language="javascript"></script>
|
||||
<!-- fonts -->
|
||||
<link rel="preload" href="fonts/BbbIcons/bbb-icons.woff?v=VERSION" as="font" crossorigin="anonymous"/>
|
||||
<link rel="preload" href="fonts/BbbIcons/bbb-icons.woff2?v=VERSION" as="font" type="font/woff2" crossorigin="anonymous"/>
|
||||
<link rel="preload" href="fonts/SourceSansPro/SourceSansPro-Light.woff?v=VERSION" as="font" crossorigin="anonymous"/>
|
||||
<link rel="preload" href="fonts/SourceSansPro/SourceSansPro-Regular.woff?v=VERSION" as="font" crossorigin="anonymous"/>
|
||||
<link rel="preload" href="fonts/SourceSansPro/SourceSansPro-Semibold.woff?v=VERSION" as="font" crossorigin="anonymous"/>
|
||||
@ -147,7 +147,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'bbb-icons';
|
||||
src: url('fonts/BbbIcons/bbb-icons.woff?v=VERSION') format('woff');
|
||||
src: url('fonts/BbbIcons/bbb-icons.woff2?v=VERSION') format('woff2'),
|
||||
url('fonts/BbbIcons/bbb-icons.woff?v=VERSION') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
@ -345,3 +345,6 @@
|
||||
.icon-bbb-pin-video_off:before {
|
||||
content: "\e965";
|
||||
}
|
||||
.icon-bbb-closed_caption_stop:before {
|
||||
content: "\e966";
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ const MEDIA = Meteor.settings.public.media;
|
||||
const DEFAULT_FULLAUDIO_MEDIA_SERVER = MEDIA.audio.fullAudioMediaServer;
|
||||
const LISTEN_ONLY_OFFERING = MEDIA.listenOnlyOffering;
|
||||
const MEDIA_TAG = MEDIA.mediaTag.replace(/#/g, '');
|
||||
const GLOBAL_AUDIO_PREFIX = 'GLOBAL_AUDIO_';
|
||||
const RECONNECT_TIMEOUT_MS = MEDIA.listenOnlyCallTimeout || 15000;
|
||||
const SENDRECV_ROLE = 'sendrecv';
|
||||
const RECV_ROLE = 'recv';
|
||||
@ -304,14 +303,9 @@ export default class SFUAudioBridge extends BaseAudioBridge {
|
||||
const { isListenOnly, extension, inputStream } = options;
|
||||
this.inEchoTest = !!extension;
|
||||
this.isListenOnly = isListenOnly;
|
||||
const callerIdName = [
|
||||
`${this.userId}_${getAudioSessionNumber()}`,
|
||||
'bbbID',
|
||||
isListenOnly ? `${GLOBAL_AUDIO_PREFIX}` : this.name,
|
||||
].join('-').replace(/"/g, "'");
|
||||
|
||||
const brokerOptions = {
|
||||
caleeName: callerIdName,
|
||||
clientSessionNumber: getAudioSessionNumber(),
|
||||
extension,
|
||||
iceServers: this.iceServers,
|
||||
mediaServer: getMediaServerAdapter(isListenOnly),
|
||||
@ -431,8 +425,7 @@ export default class SFUAudioBridge extends BaseAudioBridge {
|
||||
fetchWebRTCMappedStunTurnServers(this.sessionToken)
|
||||
.then((iceServers) => {
|
||||
const options = {
|
||||
userName: this.name,
|
||||
caleeName: `${GLOBAL_AUDIO_PREFIX}${this.voiceBridge}`,
|
||||
clientSessionNumber: getAudioSessionNumber(),
|
||||
iceServers,
|
||||
offering: LISTEN_ONLY_OFFERING,
|
||||
};
|
||||
@ -471,8 +464,11 @@ export default class SFUAudioBridge extends BaseAudioBridge {
|
||||
const mediaElement = document.getElementById(MEDIA_TAG);
|
||||
|
||||
this.clearReconnectionTimeout();
|
||||
this.broker.stop();
|
||||
this.broker = null;
|
||||
|
||||
if (this.broker) {
|
||||
this.broker.stop();
|
||||
this.broker = null;
|
||||
}
|
||||
|
||||
if (mediaElement && typeof mediaElement.pause === 'function') {
|
||||
mediaElement.pause();
|
||||
|
@ -296,7 +296,7 @@ class InputStreamLiveSelector extends Component {
|
||||
size="lg"
|
||||
circle
|
||||
accessKey={shortcuts.togglemute}
|
||||
talking={talking || undefined}
|
||||
$talking={talking || undefined}
|
||||
animations={animations}
|
||||
data-test="toggleMicrophoneButton"
|
||||
/>
|
||||
|
@ -40,15 +40,15 @@ const MuteToggleButton = styled(Button)`
|
||||
}
|
||||
`}
|
||||
|
||||
${({ talking }) => talking && `
|
||||
${({ $talking }) => $talking && `
|
||||
border-radius: 50%;
|
||||
`}
|
||||
|
||||
${({ talking, animations }) => talking && animations && css`
|
||||
|
||||
${({ $talking, animations }) => $talking && animations && css`
|
||||
animation: ${pulse} 1s infinite ease-in;
|
||||
`}
|
||||
|
||||
${({ talking, animations }) => talking && !animations && css`
|
||||
${({ $talking, animations }) => $talking && !animations && css`
|
||||
& span {
|
||||
content: '';
|
||||
outline: none !important;
|
||||
|
@ -21,7 +21,7 @@ const Icon = ({
|
||||
<i
|
||||
className={cx(className, [prependIconName, iconName].join(''))}
|
||||
// ToastContainer from react-toastify passes a useless closeToast prop here
|
||||
{..._.omit(props, ['closeToast', 'animations'])}
|
||||
{..._.omit(props, ['closeToast', 'animations', 'rotate'])}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -96,7 +96,7 @@ const Adapter = () => {
|
||||
/* needed to prevent an issue with dupĺicated messages when user role is changed
|
||||
more info: https://github.com/bigbluebutton/bigbluebutton/issues/11842 */
|
||||
useEffect(() => {
|
||||
if (users[Auth.meetingID]) {
|
||||
if (users[Auth.meetingID] && users[Auth.meetingID][Auth.userID]) {
|
||||
if (currentUserData?.role !== users[Auth.meetingID][Auth.userID].role) {
|
||||
prevUserData = currentUserData;
|
||||
}
|
||||
|
@ -242,6 +242,7 @@ class RecordingIndicator extends PureComponent {
|
||||
aria-label={`${intl.formatMessage(recording
|
||||
? intlMessages.notificationRecordingStart
|
||||
: intlMessages.notificationRecordingStop)}`}
|
||||
recording={recording}
|
||||
>
|
||||
{recordingIndicatorIcon}
|
||||
|
||||
|
@ -126,6 +126,13 @@ const RecordingIndicator = styled.div`
|
||||
|
||||
const RecordingStatusViewOnly = styled.div`
|
||||
display: flex;
|
||||
|
||||
${({ recording }) => recording && `
|
||||
padding: 5px;
|
||||
background-color: ${colorDanger};
|
||||
border: ${borderSizeLarge} solid ${colorDanger};
|
||||
border-radius: 10px;
|
||||
`}
|
||||
`;
|
||||
|
||||
export default {
|
||||
|
@ -66,9 +66,9 @@ class TalkingIndicator extends PureComponent {
|
||||
|
||||
return (
|
||||
<Styled.TalkingIndicatorButton
|
||||
spoke={!talking}
|
||||
muted={muted}
|
||||
isViewer={!amIModerator}
|
||||
$spoke={!talking || undefined}
|
||||
$muted={muted}
|
||||
$isViewer={!amIModerator || undefined}
|
||||
key={_.uniqueId(`${callerName}-`)}
|
||||
onClick={() => this.handleMuteUser(id)}
|
||||
label={callerName}
|
||||
@ -109,9 +109,9 @@ class TalkingIndicator extends PureComponent {
|
||||
|
||||
return (
|
||||
<Styled.TalkingIndicatorButton
|
||||
spoke={nobodyTalking}
|
||||
muted={false}
|
||||
isViewer={false}
|
||||
$spoke={nobodyTalking}
|
||||
$muted={false}
|
||||
$isViewer={false}
|
||||
key={_.uniqueId('_has__More_')}
|
||||
onClick={() => {}} // maybe add a dropdown to show the rest of the users
|
||||
label="..."
|
||||
|
@ -45,7 +45,7 @@ const TalkingIndicatorButton = styled(Button)`
|
||||
@media ${phoneLandscape} {
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
|
||||
i,
|
||||
span {
|
||||
position: relative;
|
||||
@ -92,7 +92,7 @@ const TalkingIndicatorButton = styled(Button)`
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
${({ spoke }) => spoke && `
|
||||
${({ $spoke }) => $spoke && `
|
||||
opacity: ${spokeOpacity};
|
||||
|
||||
[dir="rtl"] & {
|
||||
@ -100,15 +100,15 @@ const TalkingIndicatorButton = styled(Button)`
|
||||
}
|
||||
`}
|
||||
|
||||
${({ muted }) => muted && `
|
||||
${({ $muted }) => $muted && `
|
||||
cursor: default;
|
||||
|
||||
|
||||
i {
|
||||
background-color: ${colorDanger};
|
||||
}
|
||||
`}
|
||||
|
||||
${({ isViewer }) => isViewer && `
|
||||
${({ $isViewer }) => $isViewer && `
|
||||
cursor: default;
|
||||
`}
|
||||
`;
|
||||
|
@ -201,7 +201,7 @@ const VideoListItem = (props) => {
|
||||
fullscreen={isFullscreenContext}
|
||||
data-test={talking ? 'webcamItemTalkingUser' : 'webcamItem'}
|
||||
animations={animations}
|
||||
{...makeDragOperations(onVirtualBgDrop, user.userId)}
|
||||
{...makeDragOperations(onVirtualBgDrop, user?.userId)}
|
||||
>
|
||||
{
|
||||
videoIsReady
|
||||
|
@ -58,7 +58,7 @@ const DragAndDrop = (props) => {
|
||||
}, []);
|
||||
|
||||
const makeDragOperations = (onAction, userId) => {
|
||||
if (Auth.userID !== userId || !ENABLE_WEBCAM_BACKGROUND_UPLOAD) return {};
|
||||
if (!userId || Auth.userID !== userId || !ENABLE_WEBCAM_BACKGROUND_UPLOAD) return {};
|
||||
|
||||
const startAndSaveVirtualBackground = (file) => {
|
||||
const { readFile } = VirtualBgService;
|
||||
|
@ -50,6 +50,7 @@ export default function Whiteboard(props) {
|
||||
setIsZoomed,
|
||||
zoomChanger,
|
||||
isZoomed,
|
||||
isMultiUserActive,
|
||||
} = props;
|
||||
|
||||
const { pages, pageStates } = initDefaultPages(curPres?.pages.length || 1);
|
||||
@ -221,8 +222,10 @@ export default function Whiteboard(props) {
|
||||
<Cursors
|
||||
tldrawAPI={tldrawAPI}
|
||||
currentUser={currentUser}
|
||||
hasMultiUserAccess={props?.hasMultiUserAccess}
|
||||
whiteboardId={whiteboardId}
|
||||
isViewersCursorLocked={isViewersCursorLocked}
|
||||
isMultiUserActive={isMultiUserActive}
|
||||
>
|
||||
<Tldraw
|
||||
key={`wb-${!hasWBAccess && !isPresenter}`}
|
||||
@ -231,9 +234,32 @@ export default function Whiteboard(props) {
|
||||
// until we handle saving of assets in akka.
|
||||
disableAssets={true}
|
||||
onMount={(app) => {
|
||||
if (!hasWBAccess && !isPresenter) app.onPan = () => {};
|
||||
setTLDrawAPI(app);
|
||||
props.setTldrawAPI(app);
|
||||
// disable for non presenter that doesn't have multi user access
|
||||
if (!hasWBAccess && !isPresenter) {
|
||||
app.onPan = () => {};
|
||||
app.setSelectedIds = () => {};
|
||||
app.setHoveredId = () => {};
|
||||
} else {
|
||||
// disable hover highlight for background slide shape
|
||||
app.setHoveredId = (id) => {
|
||||
if (id.includes('slide-background')) return null;
|
||||
app.patchState(
|
||||
{
|
||||
document: {
|
||||
pageStates: {
|
||||
[app.getPage()?.id]: {
|
||||
hoveredId: id,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
`set_hovered_id`
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
if (curPageId) {
|
||||
app.changePage(curPageId);
|
||||
if (slidePosition.zoom === 0) {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as React from "react";
|
||||
import ReactCursorPosition from "react-cursor-position";
|
||||
import { _ } from "lodash";
|
||||
|
||||
function usePrevious(value) {
|
||||
@ -17,7 +16,9 @@ const renderCursor = (
|
||||
y,
|
||||
currentPoint,
|
||||
pageState,
|
||||
owner = false
|
||||
isMultiUserActive,
|
||||
owner = false,
|
||||
|
||||
) => {
|
||||
const z = !owner ? 2 : 1;
|
||||
let _x = null;
|
||||
@ -45,7 +46,7 @@ const renderCursor = (
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
{isMultiUserActive && <div
|
||||
style={{
|
||||
zIndex: z,
|
||||
position: "absolute",
|
||||
@ -63,19 +64,20 @@ const renderCursor = (
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</div>
|
||||
</div>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const PositionLabel = (props) => {
|
||||
const {
|
||||
position: { x = 0, y = 0 } = {},
|
||||
currentUser,
|
||||
currentPoint,
|
||||
pageState,
|
||||
publishCursorUpdate,
|
||||
whiteboardId,
|
||||
pos,
|
||||
isMultiUserActive,
|
||||
} = props;
|
||||
|
||||
const { name, color } = currentUser;
|
||||
@ -83,9 +85,7 @@ const PositionLabel = (props) => {
|
||||
|
||||
React.useEffect(() => {
|
||||
try {
|
||||
const point = _.isEqual(currentPoint, prevCurrentPoint)
|
||||
? [x, y]
|
||||
: currentPoint;
|
||||
const point = [pos.x, pos.y];
|
||||
publishCursorUpdate({
|
||||
xPercent:
|
||||
point[0] / pageState?.camera?.zoom - pageState?.camera?.point[0],
|
||||
@ -96,12 +96,12 @@ const PositionLabel = (props) => {
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}, [x, y]);
|
||||
}, [pos?.x, pos?.y]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ position: "absolute", height: "100%", width: "100%" }}>
|
||||
{renderCursor(name, color, x, y, currentPoint, props.pageState)}
|
||||
{renderCursor(name, color, pos.x, pos.y, currentPoint, props.pageState, isMultiUserActive(whiteboardId))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
@ -110,6 +110,7 @@ const PositionLabel = (props) => {
|
||||
export default function Cursors(props) {
|
||||
let cursorWrapper = React.useRef(null);
|
||||
const [active, setActive] = React.useState(false);
|
||||
const [pos, setPos] = React.useState({ x: 0, y: 0 });
|
||||
const {
|
||||
whiteboardId,
|
||||
otherCursors,
|
||||
@ -117,39 +118,80 @@ export default function Cursors(props) {
|
||||
tldrawAPI,
|
||||
publishCursorUpdate,
|
||||
children,
|
||||
isViewersCursorLocked
|
||||
isViewersCursorLocked,
|
||||
hasMultiUserAccess,
|
||||
isMultiUserActive,
|
||||
} = props;
|
||||
|
||||
const start = () => setActive(true);
|
||||
|
||||
const end = () => {
|
||||
publishCursorUpdate({
|
||||
xPercent: -1.0,
|
||||
yPercent: -1.0,
|
||||
whiteboardId: whiteboardId,
|
||||
});
|
||||
setActive(false);
|
||||
};
|
||||
|
||||
const moved = (event) => {
|
||||
const { type } = event;
|
||||
const yOffset = parseFloat(document.getElementById('Navbar')?.style?.height);
|
||||
const getSibling = (el) => el?.previousSibling || null;
|
||||
const panel = getSibling(document.getElementById('Navbar'));
|
||||
const subPanel = panel && getSibling(panel);
|
||||
const xOffset = (parseFloat(panel?.style?.width) || 0) + (parseFloat(subPanel?.style?.width) || 0);
|
||||
if (type === 'touchmove') {
|
||||
!active && setActive(true);
|
||||
return setPos({ x: event?.changedTouches[0]?.clientX - xOffset, y: event?.changedTouches[0]?.clientY - yOffset });
|
||||
}
|
||||
return setPos({ x: event.x - xOffset, y: event.y - yOffset });
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
!cursorWrapper.hasOwnProperty("mouseenter") &&
|
||||
cursorWrapper?.addEventListener("mouseenter", (event) => {
|
||||
setActive(true);
|
||||
});
|
||||
cursorWrapper?.addEventListener("mouseenter", start);
|
||||
|
||||
!cursorWrapper.hasOwnProperty("mouseleave") &&
|
||||
cursorWrapper?.addEventListener("mouseleave", (event) => {
|
||||
publishCursorUpdate({
|
||||
xPercent: -1.0,
|
||||
yPercent: -1.0,
|
||||
whiteboardId: whiteboardId,
|
||||
});
|
||||
setActive(false);
|
||||
});
|
||||
cursorWrapper?.addEventListener("mouseleave", end);
|
||||
|
||||
!cursorWrapper.hasOwnProperty("touchend") &&
|
||||
cursorWrapper?.addEventListener("touchend", end);
|
||||
|
||||
!cursorWrapper.hasOwnProperty("mousemove") &&
|
||||
cursorWrapper?.addEventListener("mousemove", moved);
|
||||
|
||||
!cursorWrapper.hasOwnProperty("touchmove") &&
|
||||
cursorWrapper?.addEventListener("touchmove", moved);
|
||||
}, [cursorWrapper]);
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
cursorWrapper.removeEventListener('mouseenter', start);
|
||||
cursorWrapper.removeEventListener('mouseleave', end);
|
||||
cursorWrapper.removeEventListener('mousemove', moved);
|
||||
cursorWrapper.removeEventListener('touchend', end);
|
||||
cursorWrapper.removeEventListener('touchmove', moved);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<span disabled={true} ref={(r) => (cursorWrapper = r)}>
|
||||
<ReactCursorPosition style={{ height: "100%", cursor: "none" }}>
|
||||
<div style={{ height: "100%", cursor: "none" }}>
|
||||
{active && (
|
||||
<PositionLabel
|
||||
pos={pos}
|
||||
otherCursors={otherCursors}
|
||||
currentUser={currentUser}
|
||||
currentPoint={tldrawAPI?.currentPoint}
|
||||
pageState={tldrawAPI?.getPageState()}
|
||||
publishCursorUpdate={publishCursorUpdate}
|
||||
whiteboardId={whiteboardId}
|
||||
isMultiUserActive={isMultiUserActive}
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</ReactCursorPosition>
|
||||
</div>
|
||||
{otherCursors
|
||||
.filter((c) => c?.xPercent && c?.yPercent)
|
||||
.filter((c) => {
|
||||
@ -159,19 +201,33 @@ export default function Cursors(props) {
|
||||
return null;
|
||||
})
|
||||
.map((c) => {
|
||||
return (
|
||||
c &&
|
||||
currentUser.userId !== c?.userId &&
|
||||
renderCursor(
|
||||
c?.userName,
|
||||
c?.presenter ? "#C70039" : "#AFE1AF",
|
||||
c?.xPercent,
|
||||
c?.yPercent,
|
||||
null,
|
||||
tldrawAPI?.getPageState(),
|
||||
true
|
||||
)
|
||||
);
|
||||
if (c && currentUser.userId !== c?.userId) {
|
||||
if (c.presenter) {
|
||||
return renderCursor(
|
||||
c?.userName,
|
||||
"#C70039",
|
||||
c?.xPercent,
|
||||
c?.yPercent,
|
||||
null,
|
||||
tldrawAPI?.getPageState(),
|
||||
isMultiUserActive(whiteboardId),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
return hasMultiUserAccess(whiteboardId, c?.userId) && (
|
||||
renderCursor(
|
||||
c?.userName,
|
||||
"#AFE1AF",
|
||||
c?.xPercent,
|
||||
c?.yPercent,
|
||||
null,
|
||||
tldrawAPI?.getPageState(),
|
||||
isMultiUserActive(whiteboardId),
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
})}
|
||||
</span>
|
||||
);
|
||||
|
@ -15,6 +15,8 @@ export default
|
||||
otherCursors: Service.getCurrentCursors(params.whiteboardId),
|
||||
tldrawAPI: params.tldrawAPI,
|
||||
isViewersCursorLocked: params.isViewersCursorLocked,
|
||||
hasMultiUserAccess: params.hasMultiUserAccess,
|
||||
isMultiUserActive: params.isMultiUserActive,
|
||||
};
|
||||
})(CursorsContainer)
|
||||
;
|
||||
|
@ -18,7 +18,7 @@ class AudioBroker extends BaseBroker {
|
||||
this.offering = true;
|
||||
|
||||
// Optional parameters are:
|
||||
// caleeName,
|
||||
// clientSessionNumber
|
||||
// iceServers,
|
||||
// offering,
|
||||
// mediaServer,
|
||||
@ -192,7 +192,7 @@ class AudioBroker extends BaseBroker {
|
||||
id: 'start',
|
||||
type: this.sfuComponent,
|
||||
role: this.role,
|
||||
caleeName: this.caleeName,
|
||||
clientSessionNumber: this.clientSessionNumber,
|
||||
sdpOffer: offer,
|
||||
mediaServer: this.mediaServer,
|
||||
extension: this.extension,
|
||||
|
@ -64,7 +64,6 @@
|
||||
"react": "^17.0.2",
|
||||
"react-autosize-textarea": "^5.0.1",
|
||||
"react-color": "^2.19.3",
|
||||
"react-cursor-position": "^3.0.3",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-draggable": "^3.3.2",
|
||||
"react-dropzone": "^7.0.1",
|
||||
|
@ -580,11 +580,11 @@ public:
|
||||
# be used in Chromium browsers. Works around the fact that Chromium has no
|
||||
# echo cancellation in non-rtc audio streams
|
||||
localEchoTest:
|
||||
enabled: false
|
||||
initialHearingState: false
|
||||
useRtcLoopbackInChromium: false
|
||||
enabled: true
|
||||
initialHearingState: true
|
||||
useRtcLoopbackInChromium: true
|
||||
# showVolumeMeter: shows an energy bar for microphones in the AudioSettings view
|
||||
showVolumeMeter: false
|
||||
showVolumeMeter: true
|
||||
stats:
|
||||
enabled: true
|
||||
interval: 10000
|
||||
|
Binary file not shown.
BIN
bigbluebutton-html5/public/fonts/BbbIcons/bbb-icons.woff2
Normal file
BIN
bigbluebutton-html5/public/fonts/BbbIcons/bbb-icons.woff2
Normal file
Binary file not shown.
@ -42,6 +42,7 @@ class ConnectionController {
|
||||
response.addHeader("User-Id", userSession.internalUserId)
|
||||
response.addHeader("Meeting-Id", userSession.meetingID)
|
||||
response.addHeader("Voice-Bridge", userSession.voicebridge )
|
||||
response.addHeader("User-Name", userSession.fullname)
|
||||
response.setStatus(200)
|
||||
response.outputStream << 'authorized'
|
||||
} else {
|
||||
|
@ -5,6 +5,7 @@ location /bbb-webrtc-sfu {
|
||||
auth_request_set $user_id $sent_http_user_id;
|
||||
auth_request_set $meeting_id $sent_http_meeting_id;
|
||||
auth_request_set $voice_bridge $sent_http_voice_bridge;
|
||||
auth_request_set $user_name $sent_http_user_name;
|
||||
|
||||
proxy_pass http://127.0.0.1:3008;
|
||||
proxy_http_version 1.1;
|
||||
@ -14,6 +15,8 @@ location /bbb-webrtc-sfu {
|
||||
proxy_set_header User-Id $user_id;
|
||||
proxy_set_header Meeting-Id $meeting_id;
|
||||
proxy_set_header Voice-Bridge $voice_bridge;
|
||||
proxy_set_header User-Name $user_name;
|
||||
|
||||
proxy_read_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
client_body_timeout 60s;
|
||||
|
@ -1,42 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -ev
|
||||
|
||||
files=`git diff --name-only HEAD..$TRAVIS_BRANCH`
|
||||
if [[ $files = *"bigbluebutton-html5"* ]]; then
|
||||
cd bigbluebutton-html5
|
||||
curl https://install.meteor.com/ | sh
|
||||
meteor npm install
|
||||
cd ..
|
||||
if [ $1 = linter ]
|
||||
then
|
||||
html5_files=""
|
||||
list=$(echo $files | tr " " "\n")
|
||||
for file in $list
|
||||
do
|
||||
if [[ $file = bigbluebutton-html5* ]] && [[ -e $file ]]
|
||||
then
|
||||
html5_files+=" $file"
|
||||
fi
|
||||
done
|
||||
|
||||
bigbluebutton-html5/node_modules/.bin/eslint --ext .jsx,.js $html5_files
|
||||
elif [ $1 = acceptance_tests ]
|
||||
then
|
||||
{
|
||||
git clone --single-branch -b update-html5 https://github.com/bigbluebutton/docker.git
|
||||
cp -r docker/{mod,restart.sh,setup.sh,supervisord.conf} .
|
||||
cp -r docker/Dockerfile Dockerfile.test
|
||||
docker build -t b2 -f Dockerfile.test .
|
||||
docker=$(docker run -d -p 80:80/tcp -p 443:443/tcp -p 1935:1935 -p 5066:5066 -p 3478:3478 -p 3478:3478/udp b2 -h localhost)
|
||||
echo $docker
|
||||
} > /dev/null
|
||||
|
||||
cd tests/puppeteer/core
|
||||
conf=$(docker exec $(docker ps -q) bbb-conf --secret | grep "Secret:")
|
||||
secret=$(echo $conf | cut -d' ' -f2)
|
||||
export BBB_SHARED_SECRET=$secret
|
||||
node html5-check.js
|
||||
cd ../../..
|
||||
npm test
|
||||
fi
|
||||
fi
|
1387
doc/message.md
1387
doc/message.md
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
||||
See [https://github.com/bigbluebutton/bigbluebutton-api-php](https://github.com/bigbluebutton/bigbluebutton-api-php).
|
53
labs/vertx-akka/.gitignore
vendored
53
labs/vertx-akka/.gitignore
vendored
@ -1,53 +0,0 @@
|
||||
.metadata
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
.history
|
||||
.worksheet
|
||||
gen
|
||||
**/*.swp
|
||||
**/*~.nib
|
||||
**/build/
|
||||
**/*.pbxuser
|
||||
**/*.perspective
|
||||
**/*.perspectivev3
|
||||
*.xcworkspace
|
||||
*.xcuserdatad
|
||||
*.iml
|
||||
project/*.ipr
|
||||
project/*.iml
|
||||
project/*.iws
|
||||
project/out
|
||||
project/*/target
|
||||
project/target
|
||||
project/*/bin
|
||||
project/*/build
|
||||
project/*.iml
|
||||
project/*/*.iml
|
||||
project/.idea
|
||||
project/.idea/*
|
||||
.idea/
|
||||
.DS_Store
|
||||
project/.DS_Store
|
||||
project/*/.DS_Store
|
||||
tm.out
|
||||
tmlog*.log
|
||||
*.tm*.epoch
|
||||
out/
|
||||
provisioning/.vagrant
|
||||
provisioning/*/.vagrant
|
||||
provisioning/*/*.known
|
||||
/sbt/akka-patterns-store/
|
||||
/daemon/src/build/
|
||||
*.lock
|
||||
logs/
|
||||
tmp/
|
||||
build/
|
||||
akka-patterns-store/
|
||||
lib_managed/
|
||||
.cache
|
||||
bin/
|
||||
.vertx/
|
||||
target/
|
||||
src/main/resources/
|
||||
|
@ -1,31 +0,0 @@
|
||||
#alignArguments=false
|
||||
alignParameters=true
|
||||
alignSingleLineCaseStatements=true
|
||||
#alignSingleLineCaseStatements.maxArrowIndent=40
|
||||
#allowParamGroupsOnNewlines=false
|
||||
#compactControlReadability=false
|
||||
#compactStringConcatenation=false
|
||||
danglingCloseParenthesis=Force
|
||||
#doubleIndentClassDeclaration=false
|
||||
doubleIndentConstructorArguments=true
|
||||
doubleIndentMethodDeclaration=true
|
||||
firstArgumentOnNewline=Force
|
||||
firstParameterOnNewline=Force
|
||||
#formatXml=true
|
||||
#indentLocalDefs=false
|
||||
#indentPackageBlocks=true
|
||||
#indentSpaces=2
|
||||
#indentWithTabs=false
|
||||
#multilineScaladocCommentsStartOnFirstLine=false
|
||||
#newlineAtEndOfFile=false
|
||||
#placeScaladocAsterisksBeneathSecondAsterisk=false
|
||||
#preserveSpaceBeforeArguments=false
|
||||
#rewriteArrowSymbols=false
|
||||
singleCasePatternOnNewline=false
|
||||
#spaceBeforeColon=false
|
||||
#spaceBeforeContextColon=false
|
||||
#spaceInsideBrackets=false
|
||||
#spaceInsideParentheses=false
|
||||
#spacesAroundMultiImports=true
|
||||
#spacesWithinPatternBinders=true
|
||||
|
@ -1,75 +0,0 @@
|
||||
import org.bigbluebutton.build._
|
||||
|
||||
import NativePackagerHelper._
|
||||
import com.typesafe.sbt.SbtNativePackager.autoImport._
|
||||
|
||||
enablePlugins(JavaServerAppPackaging)
|
||||
enablePlugins(UniversalPlugin)
|
||||
enablePlugins(DebianPlugin)
|
||||
|
||||
val compileSettings = Seq(
|
||||
organization := "org.bigbluebutton",
|
||||
|
||||
scalacOptions ++= List(
|
||||
"-unchecked",
|
||||
"-deprecation",
|
||||
"-Xlint",
|
||||
"-Ywarn-dead-code",
|
||||
"-language:_",
|
||||
"-target:jvm-1.8",
|
||||
"-encoding", "UTF-8"
|
||||
),
|
||||
javacOptions ++= List(
|
||||
"-Xlint:unchecked",
|
||||
"-Xlint:deprecation"
|
||||
)
|
||||
)
|
||||
|
||||
publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath + "/dev/repo/maven-repo/releases")))
|
||||
|
||||
// We want to have our jar files in lib_managed dir.
|
||||
// This way we'll have the right path when we import
|
||||
// into eclipse.
|
||||
retrieveManaged := true
|
||||
|
||||
testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "html", "console", "junitxml")
|
||||
|
||||
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/scalatest-reports")
|
||||
|
||||
Seq(Revolver.settings: _*)
|
||||
lazy val bbbVertxAkka = (project in file(".")).settings(name := "bbb-vertx-akka", libraryDependencies ++= Dependencies.runtime).settings(compileSettings)
|
||||
|
||||
// See https://github.com/scala-ide/scalariform
|
||||
// Config file is in ./.scalariform.conf
|
||||
scalariformAutoformat := true
|
||||
|
||||
//-----------
|
||||
// Packaging
|
||||
//
|
||||
// Reference:
|
||||
// https://github.com/muuki88/sbt-native-packager-examples/tree/master/akka-server-app
|
||||
// http://www.scala-sbt.org/sbt-native-packager/index.html
|
||||
//-----------
|
||||
mainClass := Some("org.bigbluebutton.Boot")
|
||||
|
||||
maintainer in Linux := "Richard Alam <ritzalam@gmail.com>"
|
||||
|
||||
packageSummary in Linux := "BigBlueButton Vertx Akka"
|
||||
|
||||
packageDescription := """BigBlueButton Core Vertx Akka."""
|
||||
|
||||
val user = "bigbluebutton"
|
||||
|
||||
val group = "bigbluebutton"
|
||||
|
||||
// user which will execute the application
|
||||
daemonUser in Linux := user
|
||||
|
||||
// group which will execute the application
|
||||
daemonGroup in Linux := group
|
||||
|
||||
javaOptions in Universal ++= Seq("-J-Xms130m", "-J-Xmx256m", "-Dconfig.file=conf/application.conf", "-Dlogback.configurationFile=conf/logback.xml")
|
||||
|
||||
debianPackageDependencies in Debian ++= Seq("java8-runtime-headless", "bash")
|
||||
|
||||
|
@ -1,44 +0,0 @@
|
||||
frontend ssl-in
|
||||
mode tcp
|
||||
log-format %ci:%cp\ [%t]\ %ft\ %b/%s\ %Tw/%Tc/%Tt\ %U:%B\ %ts\ %ac/%fc/%bc/%sc/%rc\ %hr\ %hs\ %sq/%bq
|
||||
|
||||
bind *:443,:::443 ssl crt /etc/ssl/ritz-ss.blindside-dev.com/ritz-ss.blindside-dev.com.pem
|
||||
|
||||
# Detect RTMP traffic
|
||||
# The first byte must be 0x03 (version 3)
|
||||
acl rtmp_handshake_ver req.payload(0,1) -m bin 03
|
||||
|
||||
# RTMP has a fixed-size handshake: 1 byte version + 1536 byte data.
|
||||
# This acl causes haproxy to not detect a request as rtmp unless
|
||||
# it's received at least that much data (and didn't match other things)
|
||||
#acl rtmp_handshake_size req.len ge 1537
|
||||
acl rtmp_handshake_size req.len ge 1
|
||||
|
||||
acl is_websocket path_beg -i /eventbus
|
||||
#acl is_websocket hdr(Upgrade) -i WebSocket
|
||||
|
||||
# haproxy has built-in HTTP detection
|
||||
|
||||
# If we haven't received enough data to identify the protocol after
|
||||
# 30 seconds, drop the connection
|
||||
tcp-request inspect-delay 30s
|
||||
|
||||
tcp-request content accept if rtmp_handshake_ver rtmp_handshake_size
|
||||
tcp-request content accept if HTTP
|
||||
|
||||
use_backend vertx if is_websocket
|
||||
use_backend red5 if rtmp_handshake_ver rtmp_handshake_size
|
||||
use_backend nginx if HTTP
|
||||
|
||||
backend nginx
|
||||
mode http
|
||||
option forwardfor
|
||||
reqadd X-Forwarded-Proto:\ https
|
||||
server nginx 127.0.0.1:80
|
||||
|
||||
backend red5
|
||||
mode tcp
|
||||
server red5 127.0.0.1:1935
|
||||
|
||||
backend vertx
|
||||
server vertx 127.0.0.1:3001
|
@ -1,13 +0,0 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name 192.168.23.33;
|
||||
|
||||
access_log /var/log/nginx/vertx-akka.access.log;
|
||||
|
||||
# Vertx-Akka landing page.
|
||||
location / {
|
||||
root /var/www/vertx-akka;
|
||||
index index.html index.htm;
|
||||
expires 1m;
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
<!--
|
||||
#%L
|
||||
distributed-chat-service
|
||||
%%
|
||||
Copyright (C) 2015 Zanclus Consulting
|
||||
%%
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
#L%
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Distributed Chat Service</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script>
|
||||
<script src="vertxbus.js"></script>
|
||||
<style>
|
||||
.inset {
|
||||
box-shadow: inset 0 0 4px #000000;
|
||||
-moz-box-shadow: inset 0 0 4px #000000;
|
||||
-webkit-box-shadow: inset 0 0 4px #000000;
|
||||
width: 400px;
|
||||
border-width: 4px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
input.inset {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
div.inset {
|
||||
height: 500px;
|
||||
white-space: pre-wrap
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
var eb = new vertx.EventBus("http://192.168.23.33:3001/eventbus");
|
||||
eb.onopen = function () {
|
||||
eb.registerHandler("chat.to.client", function (msg) {
|
||||
$('#chat').append(msg + "\n");
|
||||
});
|
||||
};
|
||||
|
||||
function send(event) {
|
||||
if (event.keyCode == 13 || event.which == 13) {
|
||||
var message = $('#input').val();
|
||||
if (message.length > 0) {
|
||||
console.log($('#input'));
|
||||
eb.publish("chat.to.server", message);
|
||||
$('#input').val("");
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div id="chat" class="inset"></div>
|
||||
<input id="input" type="text" onkeydown="send(event)" class="inset">
|
||||
</body>
|
||||
</html>
|
@ -1,35 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script>
|
||||
<script src="vertxbus.js"></script>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
.news {
|
||||
font-size: 20pt;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="news">Latest news: </div><br>
|
||||
<div id="status" class="news"></div>
|
||||
|
||||
<script>
|
||||
|
||||
var eb = new vertx.EventBus("http://192.168.23.33:3000/eventbus");
|
||||
|
||||
eb.onopen = function() {
|
||||
eb.registerHandler("news-feed", function(msg) {
|
||||
var str = "<code>" + msg + "</code><br>";
|
||||
$('#status').prepend(str);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,228 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 Red Hat, Inc.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Apache License v2.0 which accompanies this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* The Apache License v2.0 is available at
|
||||
* http://www.opensource.org/licenses/apache2.0.php
|
||||
*
|
||||
* You may elect to redistribute this code under either of these licenses.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011-2013 The original author or authors
|
||||
* ------------------------------------------------------
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* and Apache License v2.0 which accompanies this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* The Apache License v2.0 is available at
|
||||
* http://www.opensource.org/licenses/apache2.0.php
|
||||
*
|
||||
* You may elect to redistribute this code under either of these licenses.
|
||||
*/
|
||||
|
||||
var vertx = vertx || {};
|
||||
|
||||
!function(factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
// Expose as an AMD module with SockJS dependency.
|
||||
// "vertxbus" and "sockjs" names are used because
|
||||
// AMD module names are derived from file names.
|
||||
define("vertxbus", ["sockjs"], factory);
|
||||
} else {
|
||||
// No AMD-compliant loader
|
||||
factory(SockJS);
|
||||
}
|
||||
}(function(SockJS) {
|
||||
|
||||
vertx.EventBus = function(url, options) {
|
||||
|
||||
var that = this;
|
||||
var sockJSConn = new SockJS(url, undefined, options);
|
||||
var handlerMap = {};
|
||||
var replyHandlers = {};
|
||||
var state = vertx.EventBus.CONNECTING;
|
||||
var pingTimerID = null;
|
||||
var pingInterval = null;
|
||||
if (options) {
|
||||
pingInterval = options['vertxbus_ping_interval'];
|
||||
}
|
||||
if (!pingInterval) {
|
||||
pingInterval = 5000;
|
||||
}
|
||||
|
||||
that.onopen = null;
|
||||
that.onclose = null;
|
||||
|
||||
that.send = function(address, message, replyHandler) {
|
||||
sendOrPub("send", address, message, replyHandler)
|
||||
}
|
||||
|
||||
that.publish = function(address, message) {
|
||||
sendOrPub("publish", address, message, null)
|
||||
}
|
||||
|
||||
that.registerHandler = function(address, handler) {
|
||||
checkSpecified("address", 'string', address);
|
||||
checkSpecified("handler", 'function', handler);
|
||||
checkOpen();
|
||||
var handlers = handlerMap[address];
|
||||
if (!handlers) {
|
||||
handlers = [handler];
|
||||
handlerMap[address] = handlers;
|
||||
// First handler for this address so we should register the connection
|
||||
var msg = { type : "register",
|
||||
address: address };
|
||||
sockJSConn.send(JSON.stringify(msg));
|
||||
} else {
|
||||
handlers[handlers.length] = handler;
|
||||
}
|
||||
}
|
||||
|
||||
that.unregisterHandler = function(address, handler) {
|
||||
checkSpecified("address", 'string', address);
|
||||
checkSpecified("handler", 'function', handler);
|
||||
checkOpen();
|
||||
var handlers = handlerMap[address];
|
||||
if (handlers) {
|
||||
var idx = handlers.indexOf(handler);
|
||||
if (idx != -1) handlers.splice(idx, 1);
|
||||
if (handlers.length == 0) {
|
||||
// No more local handlers so we should unregister the connection
|
||||
|
||||
var msg = { type : "unregister",
|
||||
address: address};
|
||||
sockJSConn.send(JSON.stringify(msg));
|
||||
delete handlerMap[address];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
that.close = function() {
|
||||
checkOpen();
|
||||
state = vertx.EventBus.CLOSING;
|
||||
sockJSConn.close();
|
||||
}
|
||||
|
||||
that.readyState = function() {
|
||||
return state;
|
||||
}
|
||||
|
||||
sockJSConn.onopen = function() {
|
||||
// Send the first ping then send a ping every pingInterval milliseconds
|
||||
sendPing();
|
||||
pingTimerID = setInterval(sendPing, pingInterval);
|
||||
state = vertx.EventBus.OPEN;
|
||||
if (that.onopen) {
|
||||
that.onopen();
|
||||
}
|
||||
};
|
||||
|
||||
sockJSConn.onclose = function() {
|
||||
state = vertx.EventBus.CLOSED;
|
||||
if (pingTimerID) clearInterval(pingTimerID);
|
||||
if (that.onclose) {
|
||||
that.onclose();
|
||||
}
|
||||
};
|
||||
|
||||
sockJSConn.onmessage = function(e) {
|
||||
var msg = e.data;
|
||||
var json = JSON.parse(msg);
|
||||
var type = json.type;
|
||||
if (type === 'err') {
|
||||
console.error("Error received on connection: " + json.body);
|
||||
return;
|
||||
}
|
||||
var body = json.body;
|
||||
var replyAddress = json.replyAddress;
|
||||
var address = json.address;
|
||||
var replyHandler;
|
||||
if (replyAddress) {
|
||||
replyHandler = function(reply, replyHandler) {
|
||||
// Send back reply
|
||||
that.send(replyAddress, reply, replyHandler);
|
||||
};
|
||||
}
|
||||
var handlers = handlerMap[address];
|
||||
if (handlers) {
|
||||
// We make a copy since the handler might get unregistered from within the
|
||||
// handler itself, which would screw up our iteration
|
||||
var copy = handlers.slice(0);
|
||||
for (var i = 0; i < copy.length; i++) {
|
||||
copy[i](body, replyHandler);
|
||||
}
|
||||
} else {
|
||||
// Might be a reply message
|
||||
var handler = replyHandlers[address];
|
||||
if (handler) {
|
||||
delete replyHandlers[address];
|
||||
handler(body, replyHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sendPing() {
|
||||
var msg = {
|
||||
type: "ping"
|
||||
}
|
||||
sockJSConn.send(JSON.stringify(msg));
|
||||
}
|
||||
|
||||
function sendOrPub(sendOrPub, address, message, replyHandler) {
|
||||
checkSpecified("address", 'string', address);
|
||||
checkSpecified("replyHandler", 'function', replyHandler, true);
|
||||
checkOpen();
|
||||
var envelope = { type : sendOrPub,
|
||||
address: address,
|
||||
body: message };
|
||||
if (replyHandler) {
|
||||
var replyAddress = makeUUID();
|
||||
envelope.replyAddress = replyAddress;
|
||||
replyHandlers[replyAddress] = replyHandler;
|
||||
}
|
||||
var str = JSON.stringify(envelope);
|
||||
sockJSConn.send(str);
|
||||
}
|
||||
|
||||
function checkOpen() {
|
||||
if (state != vertx.EventBus.OPEN) {
|
||||
throw new Error('INVALID_STATE_ERR');
|
||||
}
|
||||
}
|
||||
|
||||
function checkSpecified(paramName, paramType, param, optional) {
|
||||
if (!optional && !param) {
|
||||
throw new Error("Parameter " + paramName + " must be specified");
|
||||
}
|
||||
if (param && typeof param != paramType) {
|
||||
throw new Error("Parameter " + paramName + " must be of type " + paramType);
|
||||
}
|
||||
}
|
||||
|
||||
function isFunction(obj) {
|
||||
return !!(obj && obj.constructor && obj.call && obj.apply);
|
||||
}
|
||||
|
||||
function makeUUID(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
|
||||
.replace(/[xy]/g,function(a,b){return b=Math.random()*16,(a=="y"?b&3|8:b|0).toString(16)})}
|
||||
|
||||
}
|
||||
|
||||
vertx.EventBus.CONNECTING = 0;
|
||||
vertx.EventBus.OPEN = 1;
|
||||
vertx.EventBus.CLOSING = 2;
|
||||
vertx.EventBus.CLOSED = 3;
|
||||
|
||||
return vertx.EventBus;
|
||||
|
||||
});
|
@ -1,99 +0,0 @@
|
||||
package org.bigbluebutton.build
|
||||
|
||||
import sbt._
|
||||
import Keys._
|
||||
|
||||
object Dependencies {
|
||||
|
||||
object Versions {
|
||||
// Scala
|
||||
val scala = "2.12.8"
|
||||
val junit = "4.12"
|
||||
val junitInterface = "0.11"
|
||||
val scalactic = "3.0.3"
|
||||
|
||||
// Libraries
|
||||
val akkaVersion = "2.5.19"
|
||||
val gson = "2.8.5"
|
||||
val jackson = "2.9.7"
|
||||
val logback = "1.2.3"
|
||||
val quicklens = "1.4.11"
|
||||
val spray = "1.3.4"
|
||||
val vertxV = "3.5.1"
|
||||
|
||||
// Apache Commons
|
||||
val lang = "3.8.1"
|
||||
val codec = "1.11"
|
||||
|
||||
// BigBlueButton
|
||||
val bbbCommons = "0.0.20-SNAPSHOT"
|
||||
|
||||
// Test
|
||||
val scalaTest = "3.0.5"
|
||||
val mockito = "2.23.0"
|
||||
val akkaTestKit = "2.5.18"
|
||||
}
|
||||
|
||||
object Compile {
|
||||
val scalaLibrary = "org.scala-lang" % "scala-library" % Versions.scala
|
||||
val scalaCompiler = "org.scala-lang" % "scala-compiler" % Versions.scala
|
||||
|
||||
val akkaActor = "com.typesafe.akka" % "akka-actor_2.12" % Versions.akkaVersion
|
||||
val akkaSl4fj = "com.typesafe.akka" % "akka-slf4j_2.12" % Versions.akkaVersion
|
||||
|
||||
val vertxWeb = "io.vertx" % "vertx-web" % Versions.vertxV
|
||||
val vertxAuthCommon = "io.vertx" % "vertx-auth-common" % Versions.vertxV
|
||||
val vertxAuthShiro = "io.vertx" % "vertx-auth-shiro" % Versions.vertxV
|
||||
val vertxWebScala = "io.vertx" %% "vertx-web-scala" % Versions.vertxV
|
||||
val vertxLangScala = "io.vertx" %% "vertx-lang-scala" % Versions.vertxV
|
||||
|
||||
val googleGson = "com.google.code.gson" % "gson" % Versions.gson
|
||||
val jacksonModule = "com.fasterxml.jackson.module" %% "jackson-module-scala" % Versions.jackson
|
||||
val quicklens = "com.softwaremill.quicklens" %% "quicklens" % Versions.quicklens
|
||||
val logback = "ch.qos.logback" % "logback-classic" % Versions.logback
|
||||
val commonsCodec = "commons-codec" % "commons-codec" % Versions.codec
|
||||
val sprayJson = "io.spray" % "spray-json_2.12" % Versions.spray
|
||||
|
||||
val redisEtaty = "com.github.etaty" % "rediscala_2.12" % "1.8.0"
|
||||
|
||||
val apacheLang = "org.apache.commons" % "commons-lang3" % Versions.lang
|
||||
|
||||
val bbbCommons = "org.bigbluebutton" % "bbb-common-message_2.12" % Versions.bbbCommons excludeAll (
|
||||
ExclusionRule(organization = "org.red5"))
|
||||
}
|
||||
|
||||
object Test {
|
||||
val scalaTest = "org.scalatest" %% "scalatest" % Versions.scalaTest % "test"
|
||||
val junit = "junit" % "junit" % Versions.junit % "test"
|
||||
val mockitoCore = "org.mockito" % "mockito-core" % Versions.mockito % "test"
|
||||
val scalactic = "org.scalactic" % "scalactic_2.12" % Versions.scalactic % "test"
|
||||
val akkaTestKit = "com.typesafe.akka" %% "akka-testkit" % Versions.akkaTestKit % "test"
|
||||
}
|
||||
|
||||
val testing = Seq(
|
||||
Test.scalaTest,
|
||||
Test.junit,
|
||||
Test.mockitoCore,
|
||||
Test.scalactic,
|
||||
Test.akkaTestKit)
|
||||
|
||||
val runtime = Seq(
|
||||
Compile.scalaLibrary,
|
||||
Compile.scalaCompiler,
|
||||
Compile.akkaActor,
|
||||
Compile.akkaSl4fj,
|
||||
Compile.vertxWeb,
|
||||
Compile.vertxAuthCommon,
|
||||
Compile.vertxAuthShiro,
|
||||
Compile.vertxWebScala,
|
||||
Compile.vertxWebScala,
|
||||
Compile.googleGson,
|
||||
Compile.jacksonModule,
|
||||
Compile.quicklens,
|
||||
Compile.logback,
|
||||
Compile.commonsCodec,
|
||||
Compile.sprayJson,
|
||||
Compile.apacheLang,
|
||||
Compile.redisEtaty,
|
||||
Compile.bbbCommons) ++ testing
|
||||
}
|
@ -1 +0,0 @@
|
||||
sbt.version=1.2.7
|
@ -1 +0,0 @@
|
||||
|
@ -1,11 +0,0 @@
|
||||
addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.1")
|
||||
|
||||
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4")
|
||||
|
||||
addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.2")
|
||||
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.15")
|
||||
|
||||
addSbtPlugin("net.vonbuchholtz" % "sbt-dependency-check" % "0.2.9")
|
||||
|
||||
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")
|
@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
rm -rf src/main/resources
|
||||
cp -R src/universal/conf src/main/resources
|
||||
sbt run
|
||||
|
@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
sbt clean stage
|
||||
sudo service bbb-vertx-akka stop
|
||||
cd target/universal/stage
|
||||
./bin/bbb-vertx-akka
|
Binary file not shown.
@ -1,12 +0,0 @@
|
||||
package org.bigbluebutton.client;
|
||||
|
||||
|
||||
import org.bigbluebutton.client.bus.ConnInfo2;
|
||||
|
||||
public interface IClientInGW {
|
||||
void connect(ConnInfo2 connInfo);
|
||||
void disconnect(ConnInfo2 connInfo);
|
||||
void handleMsgFromClient(ConnInfo2 connInfo, String json);
|
||||
void send(String channel, String json);
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class BroadcastClientMessage implements ClientMessage {
|
||||
|
||||
private String meetingID;
|
||||
private Map<String, Object> message;
|
||||
private String messageName;
|
||||
|
||||
public BroadcastClientMessage(String meetingID, String messageName, Map<String, Object> message) {
|
||||
this.meetingID = meetingID;
|
||||
this.message = message;
|
||||
this.messageName = messageName;
|
||||
}
|
||||
|
||||
public String getMeetingID() {
|
||||
return meetingID;
|
||||
}
|
||||
|
||||
public String getMessageName() {
|
||||
return messageName;
|
||||
}
|
||||
|
||||
public Map<String, Object> getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
|
||||
public class BroadcastToMeetingMsg implements ClientMessage {
|
||||
|
||||
public final String meetingId;
|
||||
public final String messageName;
|
||||
public final String json;
|
||||
|
||||
public BroadcastToMeetingMsg(String meetingId, String messageName, String json) {
|
||||
this.meetingId = meetingId;
|
||||
this.messageName = messageName;
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
public String getMessageName() {
|
||||
return messageName;
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
|
||||
public interface ClientMessage {
|
||||
|
||||
String getMessageName();
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
|
||||
public class CloseConnectionMsg implements ClientMessage {
|
||||
|
||||
public final String meetingId;
|
||||
public final String connId;
|
||||
|
||||
public CloseConnectionMsg(String meetingId, String connId) {
|
||||
this.meetingId = meetingId;
|
||||
this.connId = connId;
|
||||
}
|
||||
|
||||
public String getMessageName() {
|
||||
return "CloseConnectionMsg";
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
|
||||
public class CloseMeetingAllConnectionsMsg implements ClientMessage {
|
||||
|
||||
public final String meetingId;
|
||||
|
||||
public CloseMeetingAllConnectionsMsg(String meetingId) {
|
||||
this.meetingId = meetingId;
|
||||
}
|
||||
|
||||
public String getMessageName() {
|
||||
return "CloseMeetingAllConnectionsMsg";
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class DirectClientMessage implements ClientMessage {
|
||||
|
||||
private String meetingID;
|
||||
private String userID;
|
||||
private Map<String, Object> message;
|
||||
private String messageName;
|
||||
private String sharedObjectName;
|
||||
|
||||
public DirectClientMessage(String meetingID, String userID, String messageName, Map<String, Object> message) {
|
||||
this.meetingID = meetingID;
|
||||
this.userID = userID;
|
||||
this.message = message;
|
||||
this.messageName = messageName;
|
||||
}
|
||||
|
||||
public void setSharedObjectName(String name) {
|
||||
sharedObjectName = name;
|
||||
}
|
||||
|
||||
public String getSharedObjectName() {
|
||||
return sharedObjectName;
|
||||
}
|
||||
|
||||
public String getMeetingID() {
|
||||
return meetingID;
|
||||
}
|
||||
|
||||
public String getUserID() {
|
||||
return userID;
|
||||
}
|
||||
|
||||
public String getMessageName() {
|
||||
return messageName;
|
||||
}
|
||||
|
||||
public Map<String, Object> getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
|
||||
public class DirectToClientMsg implements ClientMessage{
|
||||
public final String meetingId;
|
||||
public final String connId;
|
||||
public final String json;
|
||||
public final String messageName;
|
||||
|
||||
public DirectToClientMsg(String meetingId, String connId, String messageName, String json) {
|
||||
this.meetingId = meetingId;
|
||||
this.connId = connId;
|
||||
this.messageName = messageName;
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
public String getMessageName() {
|
||||
return messageName;
|
||||
}
|
||||
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
public class DisconnectAllClientsMessage implements ClientMessage {
|
||||
|
||||
private final String meetingId;
|
||||
|
||||
public DisconnectAllClientsMessage(String meetingId) {
|
||||
this.meetingId = meetingId;
|
||||
}
|
||||
|
||||
public String getMeetingId() {
|
||||
return meetingId;
|
||||
}
|
||||
|
||||
public String getMessageName() {
|
||||
return "DisconnectAllClientsMessage";
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
public class DisconnectAllMessage implements ClientMessage {
|
||||
|
||||
public String getMessageName() {
|
||||
return "DisconnectAllMessage";
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
public class DisconnectClientMessage implements ClientMessage {
|
||||
|
||||
private final String meetingId;
|
||||
private final String userId;
|
||||
|
||||
public DisconnectClientMessage(String meetingId, String userId) {
|
||||
this.meetingId = meetingId;
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getMeetingId() {
|
||||
return meetingId;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public String getMessageName() {
|
||||
return "DisconnectClientMessage";
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
|
||||
public interface IConnectionInvokerService {
|
||||
void sendMessage(final ClientMessage message);
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along
|
||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package org.bigbluebutton.red5.client.messaging;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class SharedObjectClientMessage implements ClientMessage {
|
||||
public static final String BROADCAST = "broadcast";
|
||||
public static final String DIRECT = "direct";
|
||||
public static final String SHAREDOBJECT = "sharedobject";
|
||||
|
||||
private String meetingID;
|
||||
private String sharedObjectName;
|
||||
private ArrayList<Object> message;
|
||||
private String messageName;
|
||||
|
||||
public SharedObjectClientMessage(String meetingID, String sharedObjectName, String messageName, ArrayList<Object> message) {
|
||||
this.meetingID = meetingID;
|
||||
this.message = message;
|
||||
this.sharedObjectName = sharedObjectName;
|
||||
this.messageName = messageName;
|
||||
}
|
||||
|
||||
public void setSharedObjectName(String name) {
|
||||
sharedObjectName = name;
|
||||
}
|
||||
|
||||
public String getSharedObjectName() {
|
||||
return sharedObjectName;
|
||||
}
|
||||
|
||||
public String getMeetingID() {
|
||||
return meetingID;
|
||||
}
|
||||
|
||||
public String getMessageName() {
|
||||
return messageName;
|
||||
}
|
||||
|
||||
public ArrayList<Object> getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
|
||||
public class AkkaToVertxGateway implements IAkkaToVertxGateway{
|
||||
|
||||
private final Vertx vertx;
|
||||
|
||||
public AkkaToVertxGateway(Vertx vertx) {
|
||||
this.vertx = vertx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String json) {
|
||||
vertx.eventBus().publish("to-vertx", json);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.ext.auth.AuthProvider;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.handler.*;
|
||||
import io.vertx.ext.web.sstore.LocalSessionStore;
|
||||
import org.bigbluebutton.VertxToAkkaGateway;
|
||||
|
||||
public class AuthenticateVerticle extends AbstractVerticle {
|
||||
private final VertxToAkkaGateway gw;
|
||||
|
||||
public AuthenticateVerticle(VertxToAkkaGateway gw) {
|
||||
this.gw = gw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
|
||||
Router router = Router.router(vertx);
|
||||
|
||||
// We need cookies, sessions and request bodies
|
||||
router.route().handler(CookieHandler.create());
|
||||
router.route().handler(BodyHandler.create());
|
||||
router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));
|
||||
|
||||
// Simple auth service which uses a properties file for user/role info
|
||||
AuthProvider authProvider = new MyAuthProvider(vertx);
|
||||
|
||||
// We need a user session handler too to make sure the user is stored in the session between requests
|
||||
router.route().handler(UserSessionHandler.create(authProvider));
|
||||
|
||||
// Any requests to URI starting '/private/' require login
|
||||
router.route("/private/*").handler(RedirectAuthHandler.create(authProvider, "/loginpage.html"));
|
||||
|
||||
// Serve the static private pages from directory 'private'
|
||||
router.route("/private/*").handler(StaticHandler.create().setCachingEnabled(false).setWebRoot("private"));
|
||||
|
||||
// Handles the actual login
|
||||
router.route("/loginhandler").handler(FormLoginHandler.create(authProvider));
|
||||
|
||||
// Implement logout
|
||||
router.route("/logout").handler(context -> {
|
||||
context.clearUser();
|
||||
// Redirect back to the index page
|
||||
context.response().putHeader("location", "/").setStatusCode(302).end();
|
||||
});
|
||||
|
||||
// Serve the non private static pages
|
||||
router.route().handler(StaticHandler.create());
|
||||
|
||||
vertx.createHttpServer().requestHandler(router::accept).listen(4000);
|
||||
}
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.MultiMap;
|
||||
import io.vertx.core.http.HttpServerResponse;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.ext.web.handler.BodyHandler;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="http://tfox.org">Tim Fox</a>
|
||||
*/
|
||||
public class BbbApi extends AbstractVerticle {
|
||||
|
||||
|
||||
private Map<String, JsonObject> products = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
setUpInitialData();
|
||||
|
||||
Router router = Router.router(vertx);
|
||||
|
||||
router.route().handler(BodyHandler.create());
|
||||
router.get("/products/:productID").handler(this::handleGetProduct);
|
||||
router.put("/products/:productID").handler(this::handleAddProduct);
|
||||
router.get("/products").handler(this::handleListProducts);
|
||||
router.get("/bigbluebutton/api/create").handler(this::handleListProducts);
|
||||
|
||||
vertx.createHttpServer().requestHandler(router::accept).listen(4000);
|
||||
}
|
||||
|
||||
private void handleGetProduct(RoutingContext routingContext) {
|
||||
String productID = routingContext.request().getParam("productID");
|
||||
HttpServerResponse response = routingContext.response();
|
||||
if (productID == null) {
|
||||
sendError(400, response);
|
||||
} else {
|
||||
JsonObject product = products.get(productID);
|
||||
if (product == null) {
|
||||
sendError(404, response);
|
||||
} else {
|
||||
response.putHeader("content-type", "application/json").end(product.encodePrettily());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAddProduct(RoutingContext routingContext) {
|
||||
String productID = routingContext.request().getParam("productID");
|
||||
HttpServerResponse response = routingContext.response();
|
||||
if (productID == null) {
|
||||
sendError(400, response);
|
||||
} else {
|
||||
JsonObject product = routingContext.getBodyAsJson();
|
||||
if (product == null) {
|
||||
sendError(400, response);
|
||||
} else {
|
||||
products.put(productID, product);
|
||||
response.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleListProducts(RoutingContext routingContext) {
|
||||
MultiMap params = routingContext.request().params();
|
||||
System.out.println("Name: " + params.get("name"));
|
||||
JsonArray arr = new JsonArray();
|
||||
products.forEach((k, v) -> arr.add(v));
|
||||
routingContext.response().putHeader("content-type", "application/json").end(arr.encodePrettily());
|
||||
}
|
||||
|
||||
private void sendError(int statusCode, HttpServerResponse response) {
|
||||
response.setStatusCode(statusCode).end();
|
||||
}
|
||||
|
||||
private void setUpInitialData() {
|
||||
addProduct(new JsonObject().put("id", "prod3568").put("name", "Egg Whisk").put("price", 3.99).put("weight", 150));
|
||||
addProduct(new JsonObject().put("id", "prod7340").put("name", "Tea Cosy").put("price", 5.99).put("weight", 100));
|
||||
addProduct(new JsonObject().put("id", "prod8643").put("name", "Spatula").put("price", 1.00).put("weight", 80));
|
||||
}
|
||||
|
||||
private void addProduct(JsonObject product) {
|
||||
products.put(product.getString("id"), product);
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
import io.vertx.core.AsyncResult;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.ext.auth.AbstractUser;
|
||||
import io.vertx.ext.auth.AuthProvider;
|
||||
import io.vertx.core.logging.Logger;
|
||||
import io.vertx.core.logging.LoggerFactory;
|
||||
import io.vertx.core.Future;
|
||||
|
||||
public class BbbUser extends AbstractUser {
|
||||
private static final Logger log = LoggerFactory.getLogger(BbbUser.class);
|
||||
|
||||
private JsonObject jwtToken;
|
||||
private JsonArray permissions;
|
||||
|
||||
public BbbUser(JsonObject jwtToken, JsonArray permissions) {
|
||||
this.jwtToken = jwtToken;
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JsonObject principal() {
|
||||
return jwtToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthProvider(AuthProvider arg0) {
|
||||
// NOOP - JWT tokens are self contained :)
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doIsPermitted(String permission, Handler<AsyncResult<Boolean>> handler) {
|
||||
if (permissions != null) {
|
||||
for (Object jwtPermission : permissions) {
|
||||
if (permission.equals(jwtPermission)) {
|
||||
handler.handle(Future.succeededFuture(true));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("User has no permission [" + permission + "]");
|
||||
handler.handle(Future.succeededFuture(false));
|
||||
}
|
||||
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.eventbus.EventBus;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.handler.StaticHandler;
|
||||
import io.vertx.ext.bridge.BridgeEventType;
|
||||
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
|
||||
import io.vertx.ext.web.handler.sockjs.PermittedOptions;
|
||||
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
|
||||
import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
|
||||
import org.bigbluebutton.VertxToAkkaGateway;
|
||||
|
||||
|
||||
public class ChatVerticle extends AbstractVerticle {
|
||||
|
||||
private final VertxToAkkaGateway gw;
|
||||
|
||||
public ChatVerticle(VertxToAkkaGateway gw) {
|
||||
this.gw = gw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
|
||||
Router router = Router.router(vertx);
|
||||
|
||||
// Allow events for the designated addresses in/out of the event bus bridge
|
||||
BridgeOptions opts = new BridgeOptions()
|
||||
.addInboundPermitted(new PermittedOptions().setAddress("chat.to.server"))
|
||||
.addOutboundPermitted(new PermittedOptions().setAddress("chat.to.client"));
|
||||
|
||||
SockJSHandlerOptions options = new SockJSHandlerOptions().setHeartbeatInterval(2000);
|
||||
|
||||
// Create the event bus bridge and add it to the router.
|
||||
SockJSHandler ebHandler = SockJSHandler.create(vertx, options);
|
||||
router.route("/eventbus/*").handler(ebHandler);
|
||||
|
||||
// Create a router endpoint for the static content.
|
||||
router.route().handler(StaticHandler.create());
|
||||
|
||||
ebHandler.bridge(opts, be -> {
|
||||
if (be.type() == BridgeEventType.PUBLISH || be.type() == BridgeEventType.RECEIVE) {
|
||||
if (be.getRawMessage().getString("body").equals("armadillos")) {
|
||||
// Reject it
|
||||
be.complete(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
be.complete(true);
|
||||
});
|
||||
|
||||
// Start the web server and tell it to use the router to handle requests.
|
||||
vertx.createHttpServer().requestHandler(router::accept).listen(3001);
|
||||
|
||||
EventBus eb = vertx.eventBus();
|
||||
|
||||
// Register to listen for messages coming IN to the server
|
||||
eb.consumer("chat.to.server").handler(message -> {
|
||||
// Create a timestamp string
|
||||
String timestamp = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM).format(Date.from(Instant.now()));
|
||||
// Send the message back out to all clients with the timestamp prepended.
|
||||
gw.send(timestamp + ": " + message.body());
|
||||
});
|
||||
|
||||
eb.consumer("to-vertx").handler(message -> {
|
||||
eb.publish("chat.to.client", message.body());
|
||||
});
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
import org.bigbluebutton.ConnectionManager;
|
||||
import org.bigbluebutton.VertxToAkkaGateway;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
|
||||
public class HelloWorld {
|
||||
|
||||
private final Vertx vertx;
|
||||
private final VertxToAkkaGateway gw;
|
||||
private final ConnectionManager cm;
|
||||
|
||||
public HelloWorld(Vertx vertx, VertxToAkkaGateway gw, ConnectionManager cm) {
|
||||
this.vertx = vertx;
|
||||
this.gw = gw;
|
||||
this.cm = cm;
|
||||
}
|
||||
|
||||
public void startup() {
|
||||
// Create an HTTP server which simply returns "Hello World!" to each request.
|
||||
//Vertx.vertx().createHttpServer().requestHandler(req -> req.response().end("Hello World! from gradle.")).listen(3000);
|
||||
|
||||
//vertx.deployVerticle(new ChatVerticle(gw));
|
||||
//vertx.deployVerticle(new RealtimeVerticle());
|
||||
//vertx.deployVerticle(new AuthenticateVerticle());
|
||||
|
||||
//vertx.deployVerticle(new PrivateVerticle(gw));
|
||||
vertx.deployVerticle(new SockJSHandlerVerticle(cm));
|
||||
//vertx.deployVerticle(new SimpleREST());
|
||||
//vertx.deployVerticle(new BbbApi());
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
public interface IAkkaToVertxGateway {
|
||||
|
||||
void send(String json);
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
public interface IVertxToAkkaGateway {
|
||||
|
||||
void send(String json);
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
import org.bigbluebutton.VertxToAkkaGateway;
|
||||
|
||||
import io.vertx.core.AsyncResult;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.core.eventbus.DeliveryOptions;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.auth.AuthProvider;
|
||||
import io.vertx.ext.auth.User;
|
||||
import io.vertx.core.Future;
|
||||
|
||||
public class MyAuthProvider implements AuthProvider {
|
||||
|
||||
private final Vertx vertx;
|
||||
|
||||
public MyAuthProvider(Vertx vertx) {
|
||||
this.vertx = vertx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticate(JsonObject user, Handler<AsyncResult<User>> resultHandler) {
|
||||
JsonObject object = new JsonObject();
|
||||
object.put("foo", "bar").put("num", 123).put("mybool", true);
|
||||
|
||||
JsonArray array = new JsonArray();
|
||||
array.add("foo").add(123).add(false);
|
||||
|
||||
DeliveryOptions options = new DeliveryOptions();
|
||||
options.setSendTimeout(5000);
|
||||
|
||||
vertx.eventBus().send("to-akka-gw",
|
||||
"Yay! Someone kicked a ball across a patch of grass",
|
||||
options, ar -> {
|
||||
if (ar.succeeded()) {
|
||||
System.out.println("Received reply: " + ar.result().body());
|
||||
System.out.println("Got Authenticated");
|
||||
resultHandler.handle(Future.succeededFuture(new BbbUser(object, array)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
|
||||
import org.bigbluebutton.VertxToAkkaGateway;
|
||||
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.eventbus.EventBus;
|
||||
import io.vertx.core.http.HttpServerOptions;
|
||||
import io.vertx.core.net.JksOptions;
|
||||
import io.vertx.ext.auth.AuthProvider;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.Session;
|
||||
import io.vertx.ext.web.handler.*;
|
||||
import io.vertx.ext.bridge.BridgeEventType;
|
||||
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
|
||||
import io.vertx.ext.web.handler.sockjs.PermittedOptions;
|
||||
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
|
||||
import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions;
|
||||
import io.vertx.ext.web.sstore.LocalSessionStore;
|
||||
|
||||
public class PrivateVerticle extends AbstractVerticle {
|
||||
private final VertxToAkkaGateway gw;
|
||||
|
||||
public PrivateVerticle(VertxToAkkaGateway gw) {
|
||||
this.gw = gw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
|
||||
Router router = Router.router(vertx);
|
||||
|
||||
// We need cookies, sessions and request bodies
|
||||
router.route().handler(CookieHandler.create());
|
||||
router.route().handler(BodyHandler.create());
|
||||
router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));
|
||||
|
||||
// Simple auth service which uses a properties file for user/role info
|
||||
//AuthProvider authProvider = new MyAuthProvider(vertx);
|
||||
|
||||
// We need a user session handler too to make sure the user is stored in the session between requests
|
||||
//router.route().handler(UserSessionHandler.create(authProvider));
|
||||
|
||||
// Handles the actual login
|
||||
//router.route("/loginhandler").handler(FormLoginHandler.create(authProvider));
|
||||
|
||||
router.route("/private/*").handler(routingContext -> {
|
||||
|
||||
// This will require a login
|
||||
|
||||
// This will have the value true
|
||||
boolean isAuthenticated = routingContext.user() != null;
|
||||
|
||||
if (isAuthenticated) {
|
||||
System.out.println("**** User is authenticated.");
|
||||
} else {
|
||||
System.out.println("**** User is NOT authenticated.");
|
||||
}
|
||||
Session session = routingContext.session();
|
||||
|
||||
Integer cnt = session.get("hitcount");
|
||||
cnt = (cnt == null ? 0 : cnt) + 1;
|
||||
|
||||
session.put("hitcount", cnt);
|
||||
|
||||
// routingContext.response().putHeader("content-type", "text/html")
|
||||
// .end("<html><body><h1>Hitcount: " + cnt + "</h1></body></html>");
|
||||
routingContext.next();
|
||||
|
||||
});
|
||||
|
||||
// Any requests to URI starting '/private/' require login
|
||||
//router.route("/private/*").handler(RedirectAuthHandler.create(authProvider, "/loginpage.html"));
|
||||
|
||||
// Serve the static private pages from directory 'private'
|
||||
//router.route("/private/*").handler(StaticHandler.create().setCachingEnabled(false).setWebRoot("private"));
|
||||
|
||||
// Implement logout
|
||||
router.route("/logout").handler(context -> {
|
||||
context.clearUser();
|
||||
// Redirect back to the index page
|
||||
context.response().putHeader("location", "/").setStatusCode(302).end();
|
||||
});
|
||||
|
||||
// Allow events for the designated addresses in/out of the event bus bridge
|
||||
BridgeOptions opts = new BridgeOptions()
|
||||
.addInboundPermitted(new PermittedOptions().setAddress("chat.to.server"))
|
||||
.addOutboundPermitted(new PermittedOptions().setAddress("chat.to.client"));
|
||||
|
||||
SockJSHandlerOptions options = new SockJSHandlerOptions().setHeartbeatInterval(2000);
|
||||
|
||||
// Create the event bus bridge and add it to the router.
|
||||
SockJSHandler sockJSHandler = SockJSHandler.create(vertx, options);
|
||||
|
||||
router.route("/eventbus/*").handler(sockJSHandler);
|
||||
|
||||
// SockJSHandlerFactory sockJsMessageHandler = new SockJSHandlerFactory();
|
||||
// sockJsMessageHandler.setupHandler(ebHandler, opts);
|
||||
|
||||
EventBus eb = vertx.eventBus();
|
||||
|
||||
sockJSHandler.bridge(opts, be -> {
|
||||
if (be.type() == BridgeEventType.SOCKET_CREATED) {
|
||||
System.out.println("Socket create for session: " + be.socket().webSession().id() + " socketWriteId:" + be.socket().writeHandlerID());
|
||||
} else if (be.type() == BridgeEventType.SOCKET_CLOSED) {
|
||||
System.out.println("Socket closed for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
// } else if (be.type() == BridgeEventType.SOCKET_IDLE) {
|
||||
// System.out.println("Socket SOCKET_IDLE for: " + be.socket().webSession().id());
|
||||
// } else if (be.type() == BridgeEventType.SOCKET_PING) {
|
||||
// System.out.println("Socket SOCKET_PING for: " + be.socket().webSession().id());
|
||||
} else if (be.type() == BridgeEventType.UNREGISTER) {
|
||||
System.out.println("Socket UNREGISTER for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
} else if (be.type() == BridgeEventType.PUBLISH) {
|
||||
System.out.println("Socket PUBLISH for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
} else if (be.type() == BridgeEventType.RECEIVE) {
|
||||
System.out.println("Socket RECEIVE for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
} else if (be.type() == BridgeEventType.SEND) {
|
||||
System.out.println("Socket SEND for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
} else if (be.type() == BridgeEventType.REGISTER) {
|
||||
System.out.println("Socket REGISTER for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
eb.consumer("to-vertx").handler(message -> {
|
||||
System.out.println("**** response to " + be.socket().webSession().id() + " msg = " + message.body());
|
||||
if (message.body().toString().equals("CLOSE_SOCKET")) {
|
||||
be.socket().close();
|
||||
}
|
||||
});
|
||||
//gw.send(be.rawMessage().toString());
|
||||
} else {
|
||||
System.out.println("Message from: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
}
|
||||
|
||||
// System.out.println("USER=" + be.socket().webUser().principal());
|
||||
|
||||
be.complete(true);
|
||||
});
|
||||
|
||||
// Create a router endpoint for the static content.
|
||||
router.route().handler(StaticHandler.create());
|
||||
|
||||
// Start the web server and tell it to use the router to handle requests.
|
||||
//vertx.createHttpServer(new HttpServerOptions().setSsl(true).setKeyStoreOptions(
|
||||
// new JksOptions().setPath("server-keystore.jks").setPassword("wibble")
|
||||
// )).requestHandler(router::accept).listen(3001);
|
||||
vertx.createHttpServer().requestHandler(router::accept).listen(3001);
|
||||
|
||||
// Register to listen for messages coming IN to the server
|
||||
eb.consumer("chat.to.server").handler(message -> {
|
||||
// Create a timestamp string
|
||||
String timestamp = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM).format(Date.from(Instant.now()));
|
||||
// Send the message back out to all clients with the timestamp prepended.
|
||||
gw.send("TO ECHO:" + timestamp + ": " + message.body());
|
||||
// eb.publish("foofoofoo", message.body());
|
||||
});
|
||||
|
||||
eb.consumer("to-vertx").handler(message -> {
|
||||
eb.publish("chat.to.client", message.body());
|
||||
});
|
||||
|
||||
|
||||
// Serve the non private static pages
|
||||
router.route().handler(StaticHandler.create());
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.handler.StaticHandler;
|
||||
import io.vertx.ext.bridge.BridgeEventType;
|
||||
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
|
||||
import io.vertx.ext.web.handler.sockjs.PermittedOptions;
|
||||
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
|
||||
|
||||
public class RealtimeVerticle extends AbstractVerticle {
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
|
||||
Router router = Router.router(vertx);
|
||||
|
||||
// Allow outbound traffic to the news-feed address
|
||||
|
||||
BridgeOptions options = new BridgeOptions().addOutboundPermitted(new PermittedOptions().setAddress("news-feed"));
|
||||
|
||||
router.route("/eventbus/*").handler(SockJSHandler.create(vertx).bridge(options, event -> {
|
||||
|
||||
// You can also optionally provide a handler like this which will be passed any events that occur on the bridge
|
||||
// You can use this for monitoring or logging, or to change the raw messages in-flight.
|
||||
// It can also be used for fine grained access control.
|
||||
|
||||
if (event.type() == BridgeEventType.SOCKET_CREATED) {
|
||||
System.out.println("A socket was created");
|
||||
}
|
||||
|
||||
// This signals that it's ok to process the event
|
||||
event.complete(true);
|
||||
|
||||
}));
|
||||
|
||||
// Serve the static resources
|
||||
router.route().handler(StaticHandler.create());
|
||||
|
||||
vertx.createHttpServer().requestHandler(router::accept).listen(3000);
|
||||
|
||||
// Publish a message to the address "news-feed" every second
|
||||
vertx.setPeriodic(1000, t -> vertx.eventBus().publish("news-feed", "news from the server!"));
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.http.HttpServerResponse;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.ext.web.handler.BodyHandler;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="http://tfox.org">Tim Fox</a>
|
||||
*/
|
||||
public class SimpleREST extends AbstractVerticle {
|
||||
|
||||
|
||||
private Map<String, JsonObject> products = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
setUpInitialData();
|
||||
|
||||
Router router = Router.router(vertx);
|
||||
|
||||
router.route().handler(BodyHandler.create());
|
||||
router.get("/products/:productID").handler(this::handleGetProduct);
|
||||
router.put("/products/:productID").handler(this::handleAddProduct);
|
||||
router.get("/products").handler(this::handleListProducts);
|
||||
|
||||
vertx.createHttpServer().requestHandler(router::accept).listen(4000);
|
||||
}
|
||||
|
||||
private void handleGetProduct(RoutingContext routingContext) {
|
||||
String productID = routingContext.request().getParam("productID");
|
||||
HttpServerResponse response = routingContext.response();
|
||||
if (productID == null) {
|
||||
sendError(400, response);
|
||||
} else {
|
||||
JsonObject product = products.get(productID);
|
||||
if (product == null) {
|
||||
sendError(404, response);
|
||||
} else {
|
||||
response.putHeader("content-type", "application/json").end(product.encodePrettily());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAddProduct(RoutingContext routingContext) {
|
||||
String productID = routingContext.request().getParam("productID");
|
||||
HttpServerResponse response = routingContext.response();
|
||||
if (productID == null) {
|
||||
sendError(400, response);
|
||||
} else {
|
||||
JsonObject product = routingContext.getBodyAsJson();
|
||||
if (product == null) {
|
||||
sendError(400, response);
|
||||
} else {
|
||||
products.put(productID, product);
|
||||
response.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleListProducts(RoutingContext routingContext) {
|
||||
JsonArray arr = new JsonArray();
|
||||
products.forEach((k, v) -> arr.add(v));
|
||||
routingContext.response().putHeader("content-type", "application/json").end(arr.encodePrettily());
|
||||
}
|
||||
|
||||
private void sendError(int statusCode, HttpServerResponse response) {
|
||||
response.setStatusCode(statusCode).end();
|
||||
}
|
||||
|
||||
private void setUpInitialData() {
|
||||
addProduct(new JsonObject().put("id", "prod3568").put("name", "Egg Whisk").put("price", 3.99).put("weight", 150));
|
||||
addProduct(new JsonObject().put("id", "prod7340").put("name", "Tea Cosy").put("price", 5.99).put("weight", 100));
|
||||
addProduct(new JsonObject().put("id", "prod8643").put("name", "Spatula").put("price", 1.00).put("weight", 80));
|
||||
}
|
||||
|
||||
private void addProduct(JsonObject product) {
|
||||
products.put(product.getString("id"), product);
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
import io.vertx.ext.bridge.BridgeEventType;
|
||||
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
|
||||
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
|
||||
|
||||
public class SockJSHandlerFactory {
|
||||
|
||||
public SockJSHandler setupHandler(SockJSHandler ebHandler, BridgeOptions opts) {
|
||||
|
||||
ebHandler.bridge(opts, be -> {
|
||||
if (be.type() == BridgeEventType.SOCKET_CREATED) {
|
||||
System.out.println("Socket create for: " + be.socket().webSession().id());
|
||||
} else if (be.type() == BridgeEventType.SOCKET_CLOSED) {
|
||||
System.out.println("Socket closed for: " + be.socket().webSession().id());
|
||||
} else {
|
||||
System.out.println("Message from: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
}
|
||||
be.complete(true);
|
||||
});
|
||||
|
||||
return ebHandler;
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.eventbus.EventBus;
|
||||
import io.vertx.ext.bridge.BridgeEventType;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.handler.BodyHandler;
|
||||
import io.vertx.ext.web.handler.CookieHandler;
|
||||
import io.vertx.ext.web.handler.SessionHandler;
|
||||
import io.vertx.ext.web.handler.StaticHandler;
|
||||
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
|
||||
import io.vertx.ext.web.handler.sockjs.PermittedOptions;
|
||||
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
|
||||
import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions;
|
||||
import io.vertx.ext.web.sstore.LocalSessionStore;
|
||||
import org.bigbluebutton.ConnectionManager;
|
||||
|
||||
public class SockJSHandlerVerticle extends AbstractVerticle {
|
||||
private final ConnectionManager gw;
|
||||
|
||||
public SockJSHandlerVerticle(ConnectionManager gw) {
|
||||
this.gw = gw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
|
||||
Router router = Router.router(vertx);
|
||||
|
||||
// We need cookies, sessions and request bodies
|
||||
router.route().handler(CookieHandler.create());
|
||||
router.route().handler(BodyHandler.create());
|
||||
router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));
|
||||
|
||||
// Simple auth service which uses a properties file for user/role info
|
||||
//AuthProvider authProvider = new MyAuthProvider(vertx);
|
||||
|
||||
// We need a user session handler too to make sure the user is stored in the session between requests
|
||||
//router.route().handler(UserSessionHandler.create(authProvider));
|
||||
|
||||
// PermittedOptions outboundPermitted2 = new PermittedOptions().setAddressRegex("to-server-.+");
|
||||
// PermittedOptions inboundPermitted2 = new PermittedOptions().setAddressRegex("to-client-.+");
|
||||
|
||||
// Allow events for the designated addresses in/out of the event bus bridge
|
||||
BridgeOptions opts = new BridgeOptions()
|
||||
.addInboundPermitted(new PermittedOptions().setAddress("to-server"))
|
||||
.addOutboundPermitted(new PermittedOptions().setAddressRegex("to-client-.+"));
|
||||
|
||||
SockJSHandlerOptions options = new SockJSHandlerOptions().setHeartbeatInterval(2000);
|
||||
|
||||
// Create the event bus bridge and add it to the router.
|
||||
SockJSHandler sockJSHandler = SockJSHandler.create(vertx, options);
|
||||
|
||||
router.route("/eventbus/*").handler(sockJSHandler);
|
||||
|
||||
EventBus eb = vertx.eventBus();
|
||||
|
||||
sockJSHandler.bridge(opts, be -> {
|
||||
if (be.type() == BridgeEventType.SOCKET_CREATED) {
|
||||
System.out.println("Socket create for session: " + be.socket().webSession().id() + " socketWriteId:" + be.socket().writeHandlerID());
|
||||
eb.consumer(be.socket().webSession().id()).handler(message -> {
|
||||
be.socket().close();
|
||||
});
|
||||
gw.socketCreated(be.socket().webSession().id());
|
||||
} else if (be.type() == BridgeEventType.SOCKET_CLOSED) {
|
||||
System.out.println("Socket closed for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
gw.socketClosed(be.socket().webSession().id());
|
||||
eb.consumer(be.socket().webSession().id()).unregister();
|
||||
} else if (be.type() == BridgeEventType.SOCKET_IDLE) {
|
||||
System.out.println("Socket SOCKET_IDLE for: " + be.socket().webSession().id());
|
||||
} else if (be.type() == BridgeEventType.SOCKET_PING) {
|
||||
System.out.println("Socket SOCKET_PING for: " + be.socket().webSession().id());
|
||||
} else if (be.type() == BridgeEventType.UNREGISTER) {
|
||||
System.out.println("Socket UNREGISTER for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
} else if (be.type() == BridgeEventType.PUBLISH) {
|
||||
System.out.println("Socket PUBLISH for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
} else if (be.type() == BridgeEventType.RECEIVE) {
|
||||
System.out.println("Msg to Client: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
} else if (be.type() == BridgeEventType.SEND) {
|
||||
//System.out.println("Msg from Client: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
//String body = be.getRawMessage().getJsonObject("body").encode();
|
||||
gw.onMessageReceived(be.socket().webSession().id(), be.getRawMessage().getJsonObject("body"));
|
||||
} else if (be.type() == BridgeEventType.REGISTER) {
|
||||
System.out.println("Socket REGISTER for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
String address = be.getRawMessage().getString("address");
|
||||
gw.register(be.socket().webSession().id(), address);
|
||||
} else {
|
||||
System.out.println("Message from: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
|
||||
}
|
||||
|
||||
|
||||
|
||||
// System.out.println("USER=" + be.socket().webUser().principal());
|
||||
|
||||
be.complete(true);
|
||||
});
|
||||
|
||||
// Create a router endpoint for the static content.
|
||||
router.route().handler(StaticHandler.create());
|
||||
|
||||
// Start the web server and tell it to use the router to handle requests.
|
||||
//vertx.createHttpServer(new HttpServerOptions().setSsl(true).setKeyStoreOptions(
|
||||
// new JksOptions().setPath("server-keystore.jks").setPassword("wibble")
|
||||
// )).requestHandler(router::accept).listen(3001);
|
||||
vertx.createHttpServer().requestHandler(router::accept).listen(3001);
|
||||
|
||||
// Register to listen for messages coming IN to the server
|
||||
eb.consumer("to.server").handler(message -> {
|
||||
// Create a timestamp string
|
||||
// String timestamp = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM).format(Date.from(Instant.now()));
|
||||
// Send the message back out to all clients with the timestamp prepended.
|
||||
//gw.send("TO ECHO:" + timestamp + ": " + message.body());
|
||||
// eb.publish("foofoofoo", message.body());
|
||||
});
|
||||
|
||||
//eb.consumer("to-vertx").handler(message -> {
|
||||
// eb.publish("chat.to.client", message.body());
|
||||
//});
|
||||
|
||||
|
||||
// Serve the non private static pages
|
||||
router.route().handler(StaticHandler.create());
|
||||
}
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package org.bigbluebutton.vertx;
|
||||
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.core.eventbus.Message;
|
||||
import io.vertx.core.eventbus.MessageConsumer;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
|
||||
import org.bigbluebutton.VertxToAkkaGateway;
|
||||
|
||||
public class VertxToAkkaBus {
|
||||
|
||||
|
||||
public VertxToAkkaBus(Vertx vertx, VertxToAkkaGateway gw) {
|
||||
|
||||
MessageConsumer<String> consumer =
|
||||
vertx.eventBus().consumer("to-akka-gw");
|
||||
|
||||
consumer.handler(message -> {
|
||||
System.out.println("I have received a message: " + message.body());
|
||||
if (message.replyAddress() != null) {
|
||||
String replyChannel = "reply-channel";
|
||||
MessageConsumer<String> replyConsumer =
|
||||
vertx.eventBus().consumer(replyChannel);
|
||||
replyConsumer.handler(replyMessage -> {
|
||||
System.out.println("Got Authenticated");
|
||||
|
||||
message.reply(replyMessage.body().toString());
|
||||
replyConsumer.unregister();
|
||||
});
|
||||
gw.sendWithReply(message.body().toString(), replyChannel);
|
||||
} else {
|
||||
gw.send(message.body().toString());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import io.vertx.core.Vertx
|
||||
import akka.actor._
|
||||
import akka.actor.ActorLogging
|
||||
import org.bigbluebutton.vertx.IAkkaToVertxGateway
|
||||
import org.bigbluebutton.vertx.AkkaToVertxGateway
|
||||
|
||||
object AuthService {
|
||||
def props(gw: AkkaToVertxGateway): Props =
|
||||
Props(classOf[AuthService], gw)
|
||||
}
|
||||
|
||||
class AuthService(gw: AkkaToVertxGateway)
|
||||
extends Actor with ActorLogging {
|
||||
|
||||
def receive = {
|
||||
case msg: String => {
|
||||
println("****** Authenticating " + msg)
|
||||
sender ! "Let `em in!"
|
||||
}
|
||||
case _ => log.error("Cannot handle message ")
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.event.Logging
|
||||
import org.bigbluebutton.vertx.HelloWorld
|
||||
import io.vertx.core.Vertx
|
||||
import org.bigbluebutton.client.{ ClientGWApplication, MsgToClientGW }
|
||||
import org.bigbluebutton.client.bus.InternalMessageBus
|
||||
import org.bigbluebutton.vertx.AkkaToVertxGateway
|
||||
import org.bigbluebutton.vertx.VertxToAkkaBus
|
||||
|
||||
object Boot extends App with SystemConfiguration {
|
||||
|
||||
implicit val system = ActorSystem("vertx-akka-system")
|
||||
val log = Logging(system, getClass)
|
||||
|
||||
log.debug("*********** vertx-akka-system ***********************")
|
||||
|
||||
val vertx = Vertx.vertx()
|
||||
|
||||
val vertxGW = new AkkaToVertxGateway(vertx)
|
||||
|
||||
val echoActor = system.actorOf(EchoService.props(vertxGW, vertx), "echo-actor")
|
||||
val authActor = system.actorOf(AuthService.props(vertxGW), "auth-actor")
|
||||
|
||||
val akkaGW = new VertxToAkkaGateway(system, vertx, authActor, echoActor)
|
||||
val vertxToAkkaBus = new VertxToAkkaBus(vertx, akkaGW)
|
||||
val connEventBus = new InternalMessageBus
|
||||
val connectionManager = new ConnectionManager(system, vertx, connEventBus)
|
||||
val clientManager = new ClientManager(system, connEventBus)
|
||||
|
||||
val msgToClientGW = new MsgToClientGW
|
||||
val clientGW = new ClientGWApplication(system, msgToClientGW, connEventBus)
|
||||
|
||||
val hello = new HelloWorld(vertx, akkaGW, connectionManager)
|
||||
hello.startup()
|
||||
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import akka.actor.{ Actor, ActorLogging, ActorSystem, Props }
|
||||
import org.bigbluebutton.client.bus.{ ConnectionCreated, ConnectionDestroyed, InternalMessageBus }
|
||||
import org.bigbluebutton.client.Client
|
||||
|
||||
class ClientManager(system: ActorSystem, connEventBus: InternalMessageBus) {
|
||||
val actorRef = system.actorOf(ClientManagerActor.props(connEventBus), "clientMgrActor")
|
||||
}
|
||||
|
||||
object ClientManagerActor {
|
||||
val CLIENT_MANAGER_CHANNEL = "client-manager-channel"
|
||||
|
||||
def props(connEventBus: InternalMessageBus): Props = Props(classOf[ClientManagerActor], connEventBus)
|
||||
}
|
||||
|
||||
case class ClientManagerActor(connEventBus: InternalMessageBus) extends Actor with ActorLogging {
|
||||
private var clients = new collection.immutable.HashMap[String, Client]
|
||||
|
||||
def receive = {
|
||||
case m: ConnectionCreated =>
|
||||
val client = Client(m.connInfo.connId, connEventBus)
|
||||
clients += client.clientId -> client
|
||||
client.actorRef forward (m)
|
||||
case m: ConnectionDestroyed =>
|
||||
val client = clients.get(m.connInfo.connId)
|
||||
client foreach { u =>
|
||||
clients -= m.connInfo.connId
|
||||
u.actorRef forward (m)
|
||||
}
|
||||
|
||||
case _ => log.debug("***** Connection cannot handle msg ")
|
||||
}
|
||||
|
||||
override def preStart(): Unit = {
|
||||
super.preStart()
|
||||
connEventBus.subscribe(self, ClientManagerActor.CLIENT_MANAGER_CHANNEL)
|
||||
}
|
||||
|
||||
override def postStop(): Unit = {
|
||||
connEventBus.unsubscribe(self, ClientManagerActor.CLIENT_MANAGER_CHANNEL)
|
||||
super.postStop()
|
||||
}
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import akka.actor.{ Actor, ActorContext, ActorLogging, ActorRef, Props }
|
||||
import io.vertx.core.{ Handler, Vertx }
|
||||
import io.vertx.core.eventbus.{ Message, MessageConsumer }
|
||||
import io.vertx.core.json.JsonObject
|
||||
import org.bigbluebutton.client.bus.ConnInfo2
|
||||
import org.bigbluebutton.client.bus._
|
||||
|
||||
object Connection {
|
||||
def apply(connId: String, vertx: Vertx, connEventBus: InternalMessageBus)(implicit context: ActorContext): Connection = new Connection(connId, vertx, connEventBus)(context)
|
||||
}
|
||||
|
||||
class Connection(val connId: String, vertx: Vertx, connEventBus: InternalMessageBus)(implicit val context: ActorContext) {
|
||||
val actorRef = context.actorOf(ConnectionActor.props(connId, vertx, connEventBus), "connActor" + "-" + connId)
|
||||
|
||||
val consumer: MessageConsumer[JsonObject] = vertx.eventBus().consumer("from-socket-" + connId)
|
||||
consumer.handler(new MyConnHandler(actorRef))
|
||||
}
|
||||
|
||||
object ConnectionActor {
|
||||
def props(connId: String, vertx: Vertx, connEventBus: InternalMessageBus): Props = Props(classOf[ConnectionActor], connId, vertx, connEventBus)
|
||||
}
|
||||
|
||||
case class MsgFoo(msg: JsonObject)
|
||||
|
||||
class ConnectionActor(connId: String, vertx: Vertx, connEventBus: InternalMessageBus) extends Actor with ActorLogging {
|
||||
|
||||
var handshakeDone = false
|
||||
var connInfo: Option[ConnInfo2] = None
|
||||
var clientAddress: Option[String] = None
|
||||
|
||||
def receive = {
|
||||
case m: SocketDestroyed =>
|
||||
connInfo foreach { conn =>
|
||||
connEventBus.publish(MsgFromConnBusMsg(ClientManagerActor.CLIENT_MANAGER_CHANNEL, ConnectionDestroyed(conn)))
|
||||
}
|
||||
|
||||
context stop self
|
||||
case m: SocketRegister =>
|
||||
clientAddress = Some(m.channel)
|
||||
|
||||
case m: MsgFoo =>
|
||||
if (!handshakeDone) {
|
||||
for {
|
||||
conn <- getConnInfo(m.msg)
|
||||
} yield {
|
||||
println("**************************** HANDSHAKE DONE *****************************")
|
||||
handshakeDone = true
|
||||
connInfo = Some(conn)
|
||||
connEventBus.publish(MsgFromConnBusMsg(ClientManagerActor.CLIENT_MANAGER_CHANNEL, ConnectionCreated(conn)))
|
||||
|
||||
val response = buildHandshakeReply(conn.meetingId, conn.userId, conn.token)
|
||||
vertx.eventBus().publish("to-client-" + conn.token, response)
|
||||
}
|
||||
} else {
|
||||
//println("************ FORWARDING TO CLIENT ACTOR *****************************")
|
||||
connInfo foreach { conn =>
|
||||
//println("************ FORWARDING TO CLIENT ACTOR " + "clientActor-" + conn.connId + " *****************************")
|
||||
connEventBus.publish(MsgFromConnBusMsg("clientActor-" + conn.connId, MsgFromConnMsg(conn, m.msg.encode())))
|
||||
}
|
||||
}
|
||||
|
||||
case m: MsgToConnMsg =>
|
||||
//println("MsgToConnMsg " + m.json)
|
||||
connInfo foreach { conn =>
|
||||
val jsonObject = new JsonObject(m.json)
|
||||
vertx.eventBus().publish("to-client-" + conn.token, jsonObject)
|
||||
}
|
||||
|
||||
case _ => log.debug("***** Connection cannot handle msg ")
|
||||
}
|
||||
|
||||
private def getConnInfo(msg: JsonObject): Option[ConnInfo2] = {
|
||||
var conn: Option[ConnInfo2] = None
|
||||
|
||||
if (msg.containsKey("header") && msg.containsKey("body")) {
|
||||
val header = msg.getJsonObject("header")
|
||||
val body = msg.getJsonObject("body")
|
||||
if (header.containsKey("name") && header.containsKey("meetingId")
|
||||
&& header.containsKey("userId") && body.containsKey("token")) {
|
||||
val meetingId = header.getString("meetingId")
|
||||
val userId = header.getString("userId")
|
||||
val token = body.getString("token")
|
||||
conn = Some(new ConnInfo2(meetingId, userId, token, connId))
|
||||
}
|
||||
}
|
||||
|
||||
conn
|
||||
}
|
||||
|
||||
private def buildHandshakeReply(meetingId: String, userId: String, token: String): JsonObject = {
|
||||
val header: JsonObject = new JsonObject()
|
||||
header.put("name", "HandshakeReplyMessage")
|
||||
header.put("userId", userId)
|
||||
header.put("meetingId", meetingId)
|
||||
|
||||
val body = new JsonObject()
|
||||
body.put("token", token)
|
||||
|
||||
val reply = new JsonObject()
|
||||
reply.put("header", header)
|
||||
reply.put("body", body)
|
||||
|
||||
reply
|
||||
}
|
||||
|
||||
override def preStart(): Unit = {
|
||||
super.preStart()
|
||||
connEventBus.subscribe(self, "connActor-" + connId)
|
||||
}
|
||||
|
||||
override def postStop(): Unit = {
|
||||
super.postStop()
|
||||
connEventBus.unsubscribe(self, "connActor-" + connId)
|
||||
}
|
||||
}
|
||||
|
||||
class MyConnHandler(actorRef: ActorRef) extends Handler[Message[JsonObject]] {
|
||||
def handle(message: Message[JsonObject]) = {
|
||||
//println("My Handler " + message.body())
|
||||
actorRef ! (MsgFoo(message.body()))
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import akka.actor.{ Actor, ActorContext, ActorLogging, ActorSystem, Props }
|
||||
import io.vertx.core.Vertx
|
||||
import io.vertx.core.json.JsonObject
|
||||
import org.bigbluebutton.client.bus.InternalMessageBus
|
||||
|
||||
class ConnectionManager(system: ActorSystem, vertx: Vertx, connEventBus: InternalMessageBus) {
|
||||
val actorRef = system.actorOf(ConnManagerActor.props(vertx, connEventBus), "connMgrActor")
|
||||
|
||||
def socketCreated(id: String): Unit = {
|
||||
actorRef ! SocketCreated(id)
|
||||
}
|
||||
|
||||
def socketClosed(id: String): Unit = {
|
||||
actorRef ! SocketDestroyed(id)
|
||||
}
|
||||
|
||||
def register(id: String, address: String): Unit = {
|
||||
actorRef ! SocketRegister(id, address)
|
||||
}
|
||||
|
||||
def onMessageReceived(id: String, msg: JsonObject): Unit = {
|
||||
vertx.eventBus().publish("from-socket-" + id, msg)
|
||||
}
|
||||
}
|
||||
|
||||
case class SocketCreated(id: String)
|
||||
case class SocketDestroyed(id: String)
|
||||
case class SocketRegister(id: String, channel: String)
|
||||
|
||||
object ConnManagerActor {
|
||||
def props(vertx: Vertx, connEventBus: InternalMessageBus): Props = Props(classOf[ConnManagerActor], vertx, connEventBus)
|
||||
}
|
||||
|
||||
case class ConnManagerActor(vertx: Vertx, connEventBus: InternalMessageBus) extends Actor with ActorLogging {
|
||||
private var conns = new collection.immutable.HashMap[String, Connection]
|
||||
|
||||
def receive = {
|
||||
case m: SocketCreated =>
|
||||
val conn = Connection(m.id, vertx, connEventBus)
|
||||
conns += conn.connId -> conn
|
||||
case m: SocketDestroyed =>
|
||||
val conn = conns.get(m.id)
|
||||
conn foreach { u =>
|
||||
conns -= m.id
|
||||
u.actorRef forward (m)
|
||||
}
|
||||
case m: SocketRegister =>
|
||||
val conn = conns.get(m.id)
|
||||
conn foreach (u => u.actorRef forward (m))
|
||||
|
||||
case _ => log.debug("***** Connection cannot handle msg ")
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import io.vertx.core.Vertx
|
||||
import akka.actor._
|
||||
import akka.actor.ActorLogging
|
||||
import org.bigbluebutton.vertx.IAkkaToVertxGateway
|
||||
import org.bigbluebutton.vertx.AkkaToVertxGateway
|
||||
|
||||
object EchoService {
|
||||
def props(gw: AkkaToVertxGateway, vertx: Vertx): Props =
|
||||
Props(classOf[EchoService], gw, vertx)
|
||||
}
|
||||
|
||||
class EchoService(gw: AkkaToVertxGateway, vertx: Vertx) extends Actor with ActorLogging {
|
||||
|
||||
private var i: Int = 0;
|
||||
|
||||
def receive = {
|
||||
case msg: String => {
|
||||
//println("****** Echoing " + msg)
|
||||
gw.send("FROM ECHO: " + msg)
|
||||
i += 1
|
||||
if (i > 50) {
|
||||
//gw.send("CLOSE_SOCKET")
|
||||
vertx.eventBus.publish("to-vertx", "CLOSE_SOCKET")
|
||||
}
|
||||
}
|
||||
case _ => log.error("Cannot handle message ")
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user