From df71d8a6892d8bb747dca10bec79121ad35a931f Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Tue, 28 Jul 2015 18:29:00 +0000 Subject: [PATCH 1/2] - keep track of user's number of connections to red5 to manage auto-reconnect --- .../red5/BigBlueButtonApplication.java | 17 +++-- .../red5/UserConnectionMapper.java | 70 +++++++++++++++++++ 2 files changed, 83 insertions(+), 4 deletions(-) create mode 100755 bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/UserConnectionMapper.java diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java index effb9a71a1..0bb6eac33c 100755 --- a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/BigBlueButtonApplication.java @@ -42,6 +42,8 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter { private ConnectionInvokerService connInvokerService; private MessagePublisher red5InGW; + private final UserConnectionMapper userConnections = new UserConnectionMapper(); + private final String APP = "BBB"; private final String CONN = "RED5-"; @@ -166,6 +168,8 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter { log.info("User joining bbb-apps: data={}", logStr); + userConnections.addUserConnection(userId, connId); + return super.roomConnect(connection, params); } @@ -214,10 +218,15 @@ public class BigBlueButtonApplication extends MultiThreadedApplicationAdapter { Gson gson = new Gson(); String logStr = gson.toJson(logData); - log.info("User leaving bbb-apps: data={}", logStr); - - red5InGW.userLeft(bbbSession.getRoom(), getBbbSession().getInternalUserID(), sessionId); - + boolean removeUser = userConnections.userDisconnected(userId, connId); + + if (removeUser) { + log.info("User leaving bbb-apps: data={}", logStr); + red5InGW.userLeft(bbbSession.getRoom(), getBbbSession().getInternalUserID(), sessionId); + } else { + log.info("User not leaving bbb-apps but just disconnected: data={}", logStr); + } + super.roomDisconnect(conn); } diff --git a/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/UserConnectionMapper.java b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/UserConnectionMapper.java new file mode 100755 index 0000000000..7c115a8c78 --- /dev/null +++ b/bigbluebutton-apps/src/main/java/org/bigbluebutton/red5/UserConnectionMapper.java @@ -0,0 +1,70 @@ +package org.bigbluebutton.red5; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * This class maintains the connections mapping of a user. + * This tracks the connections for a user to manage auto-reconnects. + * @author ralam + * + */ +public class UserConnectionMapper { + + private ConcurrentMap users = new ConcurrentHashMap(8, 0.9f, 1);; + + /** + * Adds a connection for a user. + * @param userId + * @param connId + */ + public synchronized void addUserConnection(String userId, String connId) { + if (users.containsKey(userId)) { + UserConnection user = users.get(userId); + user.add(connId); + } else { + UserConnection user = new UserConnection(); + user.add(connId); + users.put(userId, user); + } + } + + /** + * Removed a connection for a user. Returns true if the user doesn't have any + * connection left and thus can be removed. + * @param userId + * @param connId + * @return boolean - no more connections + */ + public synchronized boolean userDisconnected(String userId, String connId) { + if (users.containsKey(userId)) { + UserConnection user = users.get(userId); + user.remove(connId); + if (user.isEmpty()) { + users.remove(userId); + return true; + } + return false; + } + + return true; + } + + private class UserConnection { + private final Set connections = new HashSet(); + + public void add(String connId) { + connections.add(connId); + } + + public void remove(String connId) { + connections.remove(connId); + } + + public boolean isEmpty() { + return connections.isEmpty(); + } + } +} From 6297a60b7aced5e11218e3926eb1d13d9c65112a Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Tue, 28 Jul 2015 19:53:46 +0000 Subject: [PATCH 2/2] - assign a presenter when there is none --- .../src/main/scala/org/bigbluebutton/core/apps/UsersApp.scala | 2 +- .../main/scala/org/bigbluebutton/core/apps/UsersModel.scala | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersApp.scala index 6b29c8d988..efedd8f1f1 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersApp.scala @@ -302,7 +302,7 @@ trait UsersApp { outGW.send(new MeetingState(mProps.meetingID, mProps.recorded, uvo.userID, meetingModel.getPermissions(), meetingModel.isMeetingMuted())) // Become presenter if the only moderator - if (usersModel.numModerators == 1) { + if ((usersModel.numModerators == 1) || (usersModel.noPresenter())) { if (ru.role == Role.MODERATOR) { assignNewPresenter(msg.userID, ru.name, msg.userID) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersModel.scala index 46b0171a3e..9fe3bdd34d 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersModel.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/UsersModel.scala @@ -96,6 +96,10 @@ class UsersModel { uservos.values find (u => u.role == MODERATOR) } + def noPresenter(): Boolean = { + !getCurrentPresenter().isDefined + } + def getCurrentPresenter(): Option[UserVO] = { uservos.values find (u => u.presenter == true) }