You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2018/02/24 17:09:05 UTC

[openmeetings] branch OPENMEETINGS-1791-quick-poll updated (5759fdd -> cf1c605)

This is an automated email from the ASF dual-hosted git repository.

solomax pushed a change to branch OPENMEETINGS-1791-quick-poll
in repository https://gitbox.apache.org/repos/asf/openmeetings.git.


    from 5759fdd  Merge branch '4.0.x' into OPENMEETINGS-1791-quick-poll
     new 11cfea4  [OPENMEETINGS-1791] hazelcast related maps are refactored
     new cf1c605  Merge branch 'OPENMEETINGS-1791-quick-poll' of github.com:apache/openmeetings into OPENMEETINGS-1791-quick-poll

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 ...pLoginManagement.java => LdapLoginManager.java} |   6 +-
 .../apache/openmeetings/core/ldap/LdapOptions.java |   6 +-
 .../openmeetings/core/remote/MobileService.java    |  26 +-
 .../core/remote/ScopeApplicationAdapter.java       |  82 ++---
 .../core/service/RecordingService.java             |  31 +-
 .../openmeetings/core/session/SessionManager.java  | 162 ---------
 .../openmeetings/core/util/WebSocketHelper.java    |   5 +-
 .../java/org/apache/openmeetings/IApplication.java |  26 --
 .../openmeetings/db/dao/server/SessiondataDao.java |   5 +-
 .../openmeetings/db/entity/basic/Client.java       |   4 +-
 .../IClientManager.java}                           |  35 +-
 .../IStreamClientManager.java}                     |  37 +-
 .../db/manager/IWhiteboardManager.java             |  12 +-
 .../apache/openmeetings/db/util/RoomHelper.java    |   4 +-
 .../java/org/apache/openmeetings/cli/Admin.java    |   4 +-
 .../service/quartz/scheduler/CleanupJob.java       |  12 +-
 .../openmeetings/service/user/UserManager.java     |  12 +-
 .../web/admin/connection/ConnectionsPanel.java     |  10 +-
 .../openmeetings/web/admin/rooms/RoomForm.java     |   4 +-
 .../apache/openmeetings/web/app/Application.java   | 398 +--------------------
 .../apache/openmeetings/web/app/ClientManager.java | 287 +++++++++++++++
 .../openmeetings/web/app/StreamClientManager.java  | 303 ++++++++++++++++
 .../apache/openmeetings/web/app/WebSession.java    |   6 +-
 .../openmeetings/web/app/WhiteboardManager.java    |  51 ++-
 .../apache/openmeetings/web/common/MainPanel.java  |   9 +-
 .../openmeetings/web/room/NicknameDialog.java      |   5 +-
 .../openmeetings/web/room/RoomBroadcaster.java     |  13 +-
 .../apache/openmeetings/web/room/RoomPanel.java    |  86 +++--
 .../web/room/RoomResourceReference.java            |   8 +-
 .../openmeetings/web/room/VideoSettings.java       |   4 +-
 .../web/room/activities/ActivitiesPanel.java       |   5 +-
 .../openmeetings/web/room/menu/RoomMenuPanel.java  |  10 +-
 .../web/room/menu/StartSharingButton.java          |  14 +-
 .../openmeetings/web/room/sidebar/RoomSidebar.java |  15 +-
 .../web/room/sidebar/icon/ClientIcon.java          |   5 +-
 .../openmeetings/web/room/wb/InterviewWbPanel.java |   6 +-
 .../apache/openmeetings/web/room/wb/WbPanel.java   |  80 +++--
 .../apache/openmeetings/web/user/chat/Chat.java    |   4 +-
 .../openmeetings/web/user/chat/ChatForm.java       |   4 +-
 .../web/user/profile/UserSearchPanel.java          |   4 +-
 .../user/record/RecordingResourceReference.java    |   8 +-
 .../openmeetings/web/user/rooms/RoomListPanel.java |   7 +-
 .../openmeetings/web/user/rooms/RoomsPanel.java    |   4 +-
 .../openmeetings/webservice/RoomWebService.java    |   5 +-
 .../openmeetings/webservice/UserWebService.java    |   3 +-
 45 files changed, 926 insertions(+), 901 deletions(-)
 rename openmeetings-core/src/main/java/org/apache/openmeetings/core/ldap/{LdapLoginManagement.java => LdapLoginManager.java} (99%)
 delete mode 100644 openmeetings-core/src/main/java/org/apache/openmeetings/core/session/SessionManager.java
 copy openmeetings-db/src/main/java/org/apache/openmeetings/db/{util/ws/TextRoomMessage.java => manager/IClientManager.java} (62%)
 rename openmeetings-db/src/main/java/org/apache/openmeetings/db/{dao/server/ISessionManager.java => manager/IStreamClientManager.java} (85%)
 copy openmeetings-core/src/test/java/org/apache/openmeetings/TestConnection.java => openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IWhiteboardManager.java (82%)
 create mode 100644 openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java
 create mode 100644 openmeetings-web/src/main/java/org/apache/openmeetings/web/app/StreamClientManager.java
 rename openmeetings-core/src/main/java/org/apache/openmeetings/core/data/whiteboard/WhiteboardCache.java => openmeetings-web/src/main/java/org/apache/openmeetings/web/app/WhiteboardManager.java (68%)

-- 
To stop receiving notification emails like this one, please contact
solomax@apache.org.

[openmeetings] 02/02: Merge branch 'OPENMEETINGS-1791-quick-poll' of github.com:apache/openmeetings into OPENMEETINGS-1791-quick-poll

Posted by so...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch OPENMEETINGS-1791-quick-poll
in repository https://gitbox.apache.org/repos/asf/openmeetings.git

commit cf1c605e182cd27e62183960aed82a254f0cf539
Merge: 11cfea4 5759fdd
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Sun Feb 25 00:08:51 2018 +0700

    Merge branch 'OPENMEETINGS-1791-quick-poll' of github.com:apache/openmeetings into OPENMEETINGS-1791-quick-poll

 CHANGELOG                                          |   96 +-
 LICENSE                                            |   14 +-
 README                                             |   28 +-
 openmeetings-core/pom.xml                          |    2 +-
 .../openmeetings/core/remote/MobileService.java    |    2 +-
 .../core/remote/ScopeApplicationAdapter.java       |   13 +-
 openmeetings-db/pom.xml                            |    2 +-
 openmeetings-flash/pom.xml                         |    2 +-
 openmeetings-flash/src/main/flex/main.mxml         |    7 +
 .../main/flex/org/apache/openmeetings/OmVideo.as   |    4 +
 openmeetings-install/pom.xml                       |    2 +-
 openmeetings-screenshare/pom.xml                   |    2 +-
 .../screenshare/gui/ScreenSharerFrame.java         |    7 +-
 openmeetings-server/pom.xml                        |    2 +-
 openmeetings-server/src/site/xdoc/JUnitTesting.xml |    2 +-
 openmeetings-server/src/site/xdoc/NewsArchive.xml  |   25 +
 openmeetings-server/src/site/xdoc/ReleaseGuide.xml |   14 +-
 openmeetings-server/src/site/xdoc/downloads.xml    |   36 +-
 openmeetings-server/src/site/xdoc/index.xml        |   39 +-
 .../src/site/xdoc/red5sip-integration_2.0.xml      |  178 ---
 .../src/site/xdoc/red5sip-integration_2.1.xml      |  325 ----
 .../src/site/xdoc/red5sip-integration_3.0.xml      |  311 ----
 .../src/site/xdoc/red5sip-integration_3.1.xml      |  305 ----
 .../src/site/xdoc/red5sip-integration_3.3.xml      |  317 ----
 .../src/site/xdoc/red5sip-integration_4.0.xml      |  316 ----
 openmeetings-server/src/site/xdoc/security.xml     |   23 +
 .../src/site/xdoc/voip-sip-integration.xml         |  310 +++-
 openmeetings-service/pom.xml                       |    4 +-
 openmeetings-util/pom.xml                          |    2 +-
 openmeetings-web/pom.xml                           |    2 +-
 .../openmeetings/web/admin/AdminBaseForm.java      |   34 +-
 .../openmeetings/web/admin/SearchableDataView.java |    2 +-
 .../openmeetings/web/admin/labels/LangForm.java    |    7 +-
 .../web/app/Application_fa.properties.xml          | 1580 ++++++++++----------
 .../apache/openmeetings/web/common/BasePanel.java  |    7 +-
 .../web/common/ConfirmableAjaxBorder.java          |    8 +-
 .../openmeetings/web/common/menu/OmMenuItem.java   |    3 +-
 .../web/common/tree/FileTreePanel.html             |   26 +-
 .../web/common/tree/FileTreePanel.java             |    4 +-
 .../web/data/SearchableDataProvider.java           |    2 +-
 .../web/room/OmRedirectTimerBehavior.java          |    2 +-
 .../apache/openmeetings/web/room/RoomPanel.java    |   11 +-
 .../org/apache/openmeetings/web/room/room-base.js  |   14 +-
 .../web/room/sidebar/RoomFilePanel.java            |    8 +
 .../openmeetings/web/room/sidebar/RoomSidebar.html |    4 +-
 .../openmeetings/web/room/sidebar/RoomSidebar.java |   14 +-
 .../org/apache/openmeetings/web/room/wb/MathJax.js |  260 ++--
 .../org/apache/openmeetings/web/room/wb/fabric.js  |  231 +--
 .../apache/openmeetings/web/room/wb/tool-util.js   |    2 +-
 .../web/user/calendar/CalendarDialog.java          |    8 +
 .../openmeetings/web/user/chat/ChatToolbar.java    |    4 +
 .../apache/openmeetings/web/user/chat/chat-base.js |   21 +-
 .../openmeetings/web/user/chat/cssemoticons.js     |  166 +-
 .../openmeetings/web/user/rooms/RoomListPanel.java |   10 +-
 .../src/main/webapp/css/cssemoticons.css           |  183 +--
 openmeetings-web/src/main/webapp/css/room.css      |   33 +-
 openmeetings-webservice/pom.xml                    |    2 +-
 pom.xml                                            |   14 +-
 58 files changed, 1844 insertions(+), 3208 deletions(-)

diff --cc openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
index 256da85,caa1026..4260bae
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
@@@ -418,10 -418,10 +418,10 @@@ public class MobileService 
  
  		final Long roomId = c.getRoomId();
  		//Sync to all users of current scope
- 		new MessageSender(scopeAdapter.getChildScope("" + roomId), "sendVarsToMessageWithClient", hsm, scopeAdapter) {
+ 		new MessageSender(scopeAdapter.getChildScope(roomId), "sendVarsToMessageWithClient", hsm, scopeAdapter) {
  			@Override
  			public boolean filter(IConnection conn) {
 -				StreamClient rcl = sessionManager.get(IClientUtil.getId(conn.getClient()));
 +				StreamClient rcl = streamClientManager.get(IClientUtil.getId(conn.getClient()));
  				return Client.Type.sharing == rcl.getType()
  						|| rcl.getRoomId() == null || !rcl.getRoomId().equals(roomId);
  			}
diff --cc openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
index fcac1ca,08ffdd4..b06e0af
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
@@@ -704,10 -697,10 +699,10 @@@ public class ScopeApplicationAdapter ex
  	}
  
  	public void sendToScope(final Long roomId, String method, Object obj) {
- 		new MessageSender(getChildScope(String.valueOf(roomId)), method, obj, this) {
+ 		new MessageSender(getChildScope(roomId), method, obj, this) {
  			@Override
  			public boolean filter(IConnection conn) {
 -				StreamClient rcl = sessionManager.get(IClientUtil.getId(conn.getClient()));
 +				StreamClient rcl = streamClientManager.get(IClientUtil.getId(conn.getClient()));
  				return rcl == null || Client.Type.sharing == rcl.getType()
  						|| rcl.getRoomId() == null || !rcl.getRoomId().equals(roomId) || userDao.get(rcl.getUserId()) == null;
  			}
diff --cc openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
index 8d4680d,8eaf87f..923d97a
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
@@@ -19,7 -19,9 +19,8 @@@
  package org.apache.openmeetings.web.room.sidebar;
  
  import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
 -import static org.apache.openmeetings.web.app.Application.getOnlineClient;
 -import static org.apache.openmeetings.web.app.Application.getRoomClients;
 +import static org.apache.openmeetings.web.app.Application.getBean;
+ import static org.apache.openmeetings.web.app.Application.kickUser;
  import static org.apache.openmeetings.web.room.RoomBroadcaster.sendUpdatedClient;
  import static org.apache.openmeetings.web.util.CallbackFunctionHelper.getNamedFunction;
  import static org.apache.wicket.ajax.attributes.CallbackParameter.explicit;

-- 
To stop receiving notification emails like this one, please contact
solomax@apache.org.

[openmeetings] 01/02: [OPENMEETINGS-1791] hazelcast related maps are refactored

Posted by so...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch OPENMEETINGS-1791-quick-poll
in repository https://gitbox.apache.org/repos/asf/openmeetings.git

commit 11cfea45e2c5c2d77aa0aa64bb57b5e5c398ac9f
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Sun Feb 25 00:04:30 2018 +0700

    [OPENMEETINGS-1791] hazelcast related maps are refactored
---
 ...pLoginManagement.java => LdapLoginManager.java} |   6 +-
 .../apache/openmeetings/core/ldap/LdapOptions.java |   6 +-
 .../openmeetings/core/remote/MobileService.java    |  26 +-
 .../core/remote/ScopeApplicationAdapter.java       |  99 +++---
 .../core/service/RecordingService.java             |  31 +-
 .../openmeetings/core/session/SessionManager.java  | 162 ---------
 .../openmeetings/core/util/WebSocketHelper.java    |   5 +-
 .../java/org/apache/openmeetings/IApplication.java |  26 --
 .../openmeetings/db/dao/server/SessiondataDao.java |   5 +-
 .../openmeetings/db/entity/basic/Client.java       |   4 +-
 .../openmeetings/db/manager/IClientManager.java    |  42 +++
 .../IStreamClientManager.java}                     |  37 +-
 .../db/manager/IWhiteboardManager.java             |  25 ++
 .../apache/openmeetings/db/util/RoomHelper.java    |   4 +-
 .../java/org/apache/openmeetings/cli/Admin.java    |   4 +-
 .../service/quartz/scheduler/CleanupJob.java       |  12 +-
 .../openmeetings/service/user/UserManager.java     |  12 +-
 .../web/admin/connection/ConnectionsPanel.java     |  10 +-
 .../openmeetings/web/admin/rooms/RoomForm.java     |  19 +-
 .../apache/openmeetings/web/app/Application.java   | 393 +--------------------
 .../apache/openmeetings/web/app/ClientManager.java | 287 +++++++++++++++
 .../openmeetings/web/app/StreamClientManager.java  | 303 ++++++++++++++++
 .../apache/openmeetings/web/app/WebSession.java    |   6 +-
 .../openmeetings/web/app/WhiteboardManager.java    |  51 ++-
 .../apache/openmeetings/web/common/MainPanel.java  |   9 +-
 .../openmeetings/web/room/NicknameDialog.java      |   5 +-
 .../openmeetings/web/room/RoomBroadcaster.java     |  13 +-
 .../apache/openmeetings/web/room/RoomPanel.java    |  86 +++--
 .../web/room/RoomResourceReference.java            |   8 +-
 .../openmeetings/web/room/VideoSettings.java       |   4 +-
 .../web/room/activities/ActivitiesPanel.java       |   5 +-
 .../openmeetings/web/room/menu/RoomMenuPanel.java  |  10 +-
 .../web/room/menu/StartSharingButton.java          |  14 +-
 .../openmeetings/web/room/sidebar/RoomSidebar.java |  15 +-
 .../web/room/sidebar/icon/ClientIcon.java          |   5 +-
 .../openmeetings/web/room/wb/InterviewWbPanel.java |   6 +-
 .../apache/openmeetings/web/room/wb/WbPanel.java   |  80 +++--
 .../apache/openmeetings/web/user/chat/Chat.java    |   4 +-
 .../openmeetings/web/user/chat/ChatForm.java       |   4 +-
 .../web/user/profile/UserSearchPanel.java          |   4 +-
 .../user/record/RecordingResourceReference.java    |   8 +-
 .../openmeetings/web/user/rooms/RoomListPanel.java |   7 +-
 .../openmeetings/web/user/rooms/RoomsPanel.java    |   4 +-
 .../openmeetings/webservice/RoomWebService.java    |   5 +-
 .../openmeetings/webservice/UserWebService.java    |   3 +-
 45 files changed, 997 insertions(+), 877 deletions(-)

diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/ldap/LdapLoginManagement.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/ldap/LdapLoginManager.java
similarity index 99%
rename from openmeetings-core/src/main/java/org/apache/openmeetings/core/ldap/LdapLoginManagement.java
rename to openmeetings-core/src/main/java/org/apache/openmeetings/core/ldap/LdapLoginManager.java
index be64e94..1b3a6a3 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/ldap/LdapLoginManagement.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/ldap/LdapLoginManager.java
@@ -79,8 +79,8 @@ import org.springframework.stereotype.Component;
  *
  */
 @Component
-public class LdapLoginManagement {
-	private static final Logger log = Red5LoggerFactory.getLogger(LdapLoginManagement.class, getWebAppRootKey());
+public class LdapLoginManager {
+	private static final Logger log = Red5LoggerFactory.getLogger(LdapLoginManager.class, getWebAppRootKey());
 	// LDAP custom attribute mapping keys
 	private static final String CONFIGKEY_LDAP_KEY_LOGIN = "ldap_user_attr_login";
 	private static final String CONFIGKEY_LDAP_KEY_LASTNAME = "ldap_user_attr_lastname";
@@ -171,7 +171,7 @@ public class LdapLoginManagement {
 	 * @throws OmException - in case of any error
 	 */
 	public User login(String _login, String passwd, Long domainId) throws OmException {
-		log.debug("LdapLoginmanagement.doLdapLogin");
+		log.debug("LdapLoginmanager.doLdapLogin");
 		if (!userDao.validLogin(_login)) {
 			log.error("Invalid login provided");
 			return null;
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/ldap/LdapOptions.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/ldap/LdapOptions.java
index 8c9d542..95851ee 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/ldap/LdapOptions.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/ldap/LdapOptions.java
@@ -25,9 +25,9 @@ import java.util.Properties;
 
 import org.apache.directory.api.ldap.model.message.AliasDerefMode;
 import org.apache.directory.api.ldap.model.message.SearchScope;
-import org.apache.openmeetings.core.ldap.LdapLoginManagement.AuthType;
-import org.apache.openmeetings.core.ldap.LdapLoginManagement.GroupMode;
-import org.apache.openmeetings.core.ldap.LdapLoginManagement.Provisionning;
+import org.apache.openmeetings.core.ldap.LdapLoginManager.AuthType;
+import org.apache.openmeetings.core.ldap.LdapLoginManager.GroupMode;
+import org.apache.openmeetings.core.ldap.LdapLoginManager.Provisionning;
 import org.red5.logging.Red5LoggerFactory;
 import org.slf4j.Logger;
 
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
index ef36015..256da85 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
@@ -51,7 +51,6 @@ import org.apache.openmeetings.db.dao.basic.ChatDao;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.label.LabelDao;
 import org.apache.openmeetings.db.dao.room.RoomDao;
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.dao.server.SessiondataDao;
 import org.apache.openmeetings.db.dao.user.IUserManager;
 import org.apache.openmeetings.db.dao.user.UserDao;
@@ -64,6 +63,7 @@ import org.apache.openmeetings.db.entity.server.Sessiondata;
 import org.apache.openmeetings.db.entity.user.Group;
 import org.apache.openmeetings.db.entity.user.GroupUser;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.db.manager.IStreamClientManager;
 import org.apache.openmeetings.db.util.FormatHelper;
 import org.apache.openmeetings.util.OmException;
 import org.apache.wicket.util.string.Strings;
@@ -87,7 +87,7 @@ public class MobileService {
 	@Autowired
 	private SessiondataDao sessionDao;
 	@Autowired
-	private ISessionManager sessionManager;
+	private IStreamClientManager streamClientManager;
 	@Autowired
 	private RoomDao roomDao;
 	@Autowired
@@ -249,7 +249,7 @@ public class MobileService {
 			Sessiondata sd = sessionDao.create(u.getId(), u.getLanguageId());
 			StreamClient c = create(u, sd);
 			c.setScope(conn.getScope().getName());
-			sessionManager.add(c);
+			streamClientManager.add(c);
 			IClientUtil.init(conn.getClient(), c.getUid(), false);
 
 			add(result, "sid", sd.getSessionId());
@@ -272,7 +272,7 @@ public class MobileService {
 		IConnection current = Red5.getConnectionLocal();
 		for (IConnection conn : current.getScope().getClientConnections()) {
 			if (conn != null && conn instanceof IServiceCapableConnection) {
-				StreamClient c = sessionManager.get(IClientUtil.getId(conn.getClient()));
+				StreamClient c = streamClientManager.get(IClientUtil.getId(conn.getClient()));
 				if (!Strings.isEmpty(c.getAvsettings()) && Client.Type.sharing != c.getType()) {
 					Map<String, Object> map = new HashMap<>();
 					add(map, "streamId", c.getId());
@@ -304,7 +304,7 @@ public class MobileService {
 			room.put("org", org);
 		}
 		room.put("first", first);
-		room.put("users", sessionManager.listByRoom(r.getId()).size());
+		room.put("users", streamClientManager.list(r.getId()).size());
 		room.put("total", r.getCapacity());
 		room.put("audioOnly", r.isAudioOnly());
 		result.add(room);
@@ -313,7 +313,7 @@ public class MobileService {
 	public List<Map<String, Object>> getRooms() {
 		List<Map<String, Object>> result = new ArrayList<>();
 		IConnection current = Red5.getConnectionLocal();
-		StreamClient c = sessionManager.get(IClientUtil.getId(current.getClient()));
+		StreamClient c = streamClientManager.get(IClientUtil.getId(current.getClient()));
 		User u = userDao.get(c.getUserId());
 		if (cfgDao.getBool(CONFIG_MYROOMS_ENABLED, true)) {
 			//my rooms
@@ -353,7 +353,7 @@ public class MobileService {
 	public Map<String, Object> roomConnect(String sid, Long userId) {
 		// publicSid is changed on mobile room connect
 		IConnection current = Red5.getConnectionLocal();
-		StreamClient c = sessionManager.get(IClientUtil.getId(current.getClient()));
+		StreamClient c = streamClientManager.get(IClientUtil.getId(current.getClient()));
 		Map<String, Object> result = new HashMap<>();
 		result.put("publicSid", c.getUid());
 		result.put("broadCastId", c.getBroadcastId());
@@ -362,7 +362,7 @@ public class MobileService {
 
 	public Map<String, Object> updateAvMode(String avMode, String width, String height, Integer interviewPodId) {
 		IConnection current = Red5.getConnectionLocal();
-		StreamClient c = sessionManager.get(IClientUtil.getId(current.getClient()));
+		StreamClient c = streamClientManager.get(IClientUtil.getId(current.getClient()));
 		c.setAvsettings(avMode);
 		if (!"n".equals(avMode)) {
 			c.setBroadcastId(UUID.randomUUID().toString());
@@ -373,7 +373,7 @@ public class MobileService {
 		if (interviewPodId > 0) {
 			c.setInterviewPodId(interviewPodId);
 		}
-		sessionManager.update(c);
+		streamClientManager.update(c);
 		Map<String, Object> hsm = new HashMap<>();
 		hsm.put("client", c);
 		hsm.put("message", new String[]{"avsettings", "0", avMode});
@@ -388,7 +388,7 @@ public class MobileService {
 
 	public void sendChatMessage(String msg) {
 		IConnection current = Red5.getConnectionLocal();
-		StreamClient c = sessionManager.get(IClientUtil.getId(current.getClient()));
+		StreamClient c = streamClientManager.get(IClientUtil.getId(current.getClient()));
 
 		ChatMessage m = new ChatMessage();
 		m.setMessage(msg);
@@ -405,7 +405,7 @@ public class MobileService {
 	}
 
 	public void sendChatMessage(String uid, ChatMessage m, FastDateFormat fmt) {
-		sendChatMessage(sessionManager.get(uid), m, fmt);
+		sendChatMessage(streamClientManager.get(uid), m, fmt);
 	}
 
 	public void sendChatMessage(StreamClient c, ChatMessage m, FastDateFormat fmt) {
@@ -421,7 +421,7 @@ public class MobileService {
 		new MessageSender(scopeAdapter.getChildScope("" + roomId), "sendVarsToMessageWithClient", hsm, scopeAdapter) {
 			@Override
 			public boolean filter(IConnection conn) {
-				StreamClient rcl = sessionManager.get(IClientUtil.getId(conn.getClient()));
+				StreamClient rcl = streamClientManager.get(IClientUtil.getId(conn.getClient()));
 				return Client.Type.sharing == rcl.getType()
 						|| rcl.getRoomId() == null || !rcl.getRoomId().equals(roomId);
 			}
@@ -444,7 +444,7 @@ public class MobileService {
 		List<Map<String,Object>> myChatList = new ArrayList<>();
 		try {
 			IConnection current = Red5.getConnectionLocal();
-			StreamClient c = sessionManager.get(IClientUtil.getId(current.getClient()));
+			StreamClient c = streamClientManager.get(IClientUtil.getId(current.getClient()));
 			Long roomId = c.getRoomId();
 
 			log.debug("GET CHATROOM: " + roomId);
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
index 8434faf..fcac1ca 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
@@ -46,13 +46,14 @@ import org.apache.openmeetings.db.dao.log.ConferenceLogDao;
 import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.dao.room.RoomDao;
 import org.apache.openmeetings.db.dao.room.SipDao;
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.dto.room.CheckDto;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.log.ConferenceLog;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.StreamClient;
+import org.apache.openmeetings.db.manager.IClientManager;
+import org.apache.openmeetings.db.manager.IStreamClientManager;
 import org.apache.openmeetings.db.util.ws.RoomMessage;
 import org.apache.openmeetings.db.util.ws.TextRoomMessage;
 import org.apache.openmeetings.util.NullStringer;
@@ -88,7 +89,9 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 	private static final String HEIGHT_PARAM = "height";
 
 	@Autowired
-	private ISessionManager sessionManager;
+	private IStreamClientManager streamClientManager;
+	@Autowired
+	private IClientManager clientManager;
 	@Autowired
 	private RecordingService recordingService;
 	@Autowired
@@ -213,7 +216,6 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 		rcm.setRemoteAddress(conn.getRemoteAddress());
 		rcm.setSwfurl(swfURL);
 		rcm.setTcUrl(tcUrl);
-		IApplication iapp = getApp();
 		Number width = (Number)connParams.get(WIDTH_PARAM);
 		Number height = (Number)connParams.get(HEIGHT_PARAM);
 		if (width != null && height != null) {
@@ -224,7 +226,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 		if (map.containsKey("screenClient")) {
 			rcm.setType(Client.Type.sharing);
 		}
-		rcm = sessionManager.add(iapp.updateClient(rcm, false));
+		rcm = streamClientManager.add(streamClientManager.update(rcm, false));
 		if (rcm == null) {
 			_log.warn("Failed to create Client on room connect");
 			return false;
@@ -240,13 +242,28 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 		return true;
 	}
 
+	@Override
+	public IScope getChildScope(String name) {
+		IScope sc = null;
+		try {
+			sc = super.getChildScope(name);
+		} catch (Exception e) {
+			//no-op, scope doesn't exist while testing
+		}
+		return sc;
+	}
+
+	public IScope getChildScope(Long roomId) {
+		return getChildScope(String.valueOf(roomId));
+	}
+
 	public Map<String, String> screenSharerAction(Map<String, Object> map) {
 		Map<String, String> returnMap = new HashMap<>();
 		try {
 			_log.debug("-----------  screenSharerAction ENTER");
 			IConnection current = Red5.getConnectionLocal();
 
-			StreamClient client = sessionManager.get(IClientUtil.getId(current.getClient()));
+			StreamClient client = streamClientManager.get(IClientUtil.getId(current.getClient()));
 
 			if (client != null) {
 				boolean changed = false;
@@ -277,7 +294,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 				}
 
 				if (changed) {
-					sessionManager.update(client);
+					streamClientManager.update(client);
 
 					if (!client.isSharingStarted() && !client.isRecordingStarted() && !client.isPublishStarted()) {
 						returnMap.put("result", "stopAll");
@@ -302,11 +319,11 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 			_log.debug("-----------  setConnectionAsSharingClient");
 			IConnection current = Red5.getConnectionLocal();
 
-			StreamClient client = sessionManager.get(IClientUtil.getId(current.getClient()));
+			StreamClient client = streamClientManager.get(IClientUtil.getId(current.getClient()));
 
 			if (client != null) {
-				boolean startRecording = Boolean.parseBoolean("" + map.get("startRecording")) && (0 == sessionManager.getRecordingCount(client.getRoomId()));
-				boolean startStreaming = Boolean.parseBoolean("" + map.get("startStreaming")) && (0 == sessionManager.getSharingCount(client.getRoomId()));
+				boolean startRecording = Boolean.parseBoolean("" + map.get("startRecording")) && (0 == streamClientManager.getRecordingCount(client.getRoomId()));
+				boolean startStreaming = Boolean.parseBoolean("" + map.get("startStreaming")) && (0 == streamClientManager.getSharingCount(client.getRoomId()));
 				boolean startPublishing = Boolean.parseBoolean("" + map.get("startPublishing"));
 
 				boolean alreadyStreaming = client.isSharingStarted();
@@ -324,7 +341,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 
 				client.setWidth(Double.valueOf("" + map.get("screenWidth")).intValue());
 				client.setHeight(Double.valueOf("" + map.get("screenHeight")).intValue());
-				sessionManager.update(client);
+				streamClientManager.update(client);
 
 				Map<String, Object> returnMap = new HashMap<>();
 				returnMap.put("alreadyPublished", alreadyPublishing);
@@ -376,7 +393,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 		try {
 			_log.debug("[roomLeave] {} {} {} {}", client.getId(), room.getClients().size(), room.getContextPath(), room.getName());
 
-			StreamClient rcl = sessionManager.get(IClientUtil.getId(client));
+			StreamClient rcl = streamClientManager.get(IClientUtil.getId(client));
 
 			// The Room Client can be null if the Client left the room by using
 			// logicalRoomLeave
@@ -400,7 +417,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 		new MessageSender(scope, "stopStream", new Object(), this) {
 			@Override
 			public boolean filter(IConnection conn) {
-				StreamClient rcl = sessionManager.get(IClientUtil.getId(conn.getClient()));
+				StreamClient rcl = streamClientManager.get(IClientUtil.getId(conn.getClient()));
 				return rcl == null
 						|| Client.Type.sharing != rcl.getType()
 						|| !c.getSid().equals(rcl.getSid());
@@ -409,8 +426,8 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 	}
 
 	public void roomLeaveByScope(org.apache.openmeetings.db.entity.basic.IClient c, Long roomId) {
-		StreamClient rcl = sessionManager.get(c.getUid());
-		IScope scope = getChildScope(String.valueOf(roomId));
+		StreamClient rcl = streamClientManager.get(c.getUid());
+		IScope scope = getChildScope(roomId);
 		_log.debug("[roomLeaveByScope] {} {} {} {}", c.getUid(), roomId, rcl, scope);
 		if (rcl != null && scope != null) {
 			roomLeaveByScope(rcl, scope);
@@ -453,9 +470,9 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 			_log.debug("currentScope " + scope);
 
 			if (Client.Type.mobile == client.getType() || Client.Type.sip == client.getType()) {
-				getApp().exit(client);
+				clientManager.exit(client);
 			}
-			sessionManager.remove(client.getUid());
+			streamClientManager.remove(client.getUid());
 		} catch (Exception err) {
 			_log.error("[roomLeaveByScope]", err);
 		}
@@ -474,7 +491,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 		try {
 			_log.debug("-----------  streamPublishStart");
 			IConnection current = Red5.getConnectionLocal();
-			final StreamClient c = sessionManager.get(IClientUtil.getId(current.getClient()));
+			final StreamClient c = streamClientManager.get(IClientUtil.getId(current.getClient()));
 
 			// Notify all the clients that the stream had been started
 			String streamName = stream.getPublishedName();
@@ -490,13 +507,13 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 				}
 			}
 			if (Client.Type.sip == c.getType()) {
-				Client cl = getApp().getOmClientBySid(c.getSid());
+				Client cl = clientManager.getBySid(c.getSid());
 				String newNumber = getSipTransportLastname(c.getRoomId());
 				cl.getUser().setLastname(newNumber);
 				c.setLastname(newNumber);
 				c.setLastname(getSipTransportLastname(c.getRoomId()));
 			}
-			sessionManager.update(c);
+			streamClientManager.update(c);
 			if (Client.Type.sharing == c.getType() && c.isRecordingStarted()) {
 				recordingService.startRecording(current.getScope(), c, false);
 			}
@@ -508,7 +525,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 			new MessageSender(current, "newStream", c, this) {
 				@Override
 				public boolean filter(IConnection conn) {
-					StreamClient rcl = sessionManager.get(IClientUtil.getId(conn.getClient()));
+					StreamClient rcl = streamClientManager.get(IClientUtil.getId(conn.getClient()));
 
 					if (rcl == null || Strings.isEmpty(rcl.getUid())) {
 						_log.debug("Invalid client");
@@ -554,7 +571,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 		_log.debug("start streamBroadcastClose broadcast close: {}", stream.getPublishedName());
 		try {
 			IConnection current = Red5.getConnectionLocal();
-			StreamClient rcl = sessionManager.get(IClientUtil.getId(current.getClient()));
+			StreamClient rcl = streamClientManager.get(IClientUtil.getId(current.getClient()));
 
 			if (rcl == null) {
 
@@ -579,10 +596,10 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 				rcl.setBroadcasting(false);
 				rcl.setAvsettings("n");
 			}
-			sessionManager.update(rcl);
+			streamClientManager.update(rcl);
 			Room r = roomDao.get(rcl.getRoomId());
 			if ((Client.Type.sharing == rcl.getType() && rcl.isRecordingStarted())
-					|| (r != null && Room.Type.interview == r.getType() && sessionManager.getBroadcastingCount(rcl.getRoomId()) == 0))
+					|| (r != null && Room.Type.interview == r.getType() && streamClientManager.getBroadcastingCount(rcl.getRoomId()) == 0))
 			{
 				_log.debug("*** Screen sharing client stoped recording, or last broadcasting user stoped in interview room");
 				recordingService.stopRecording(scope, rcl);
@@ -600,7 +617,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 	public void setNewCursorPosition(Double x, Double y) {
 		try {
 			IConnection current = Red5.getConnectionLocal();
-			StreamClient c = sessionManager.get(IClientUtil.getId(current.getClient()));
+			StreamClient c = streamClientManager.get(IClientUtil.getId(current.getClient()));
 
 			sendMessageToCurrentScope("newScreenCursor", new Object[] {c.getUid(), x, y}, true, false);
 		} catch (Exception err) {
@@ -626,13 +643,13 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 		try {
 			_log.debug("-----------  switchMicMuted: " + publicSID);
 
-			StreamClient currentClient = sessionManager.get(publicSID);
+			StreamClient currentClient = streamClientManager.get(publicSID);
 			if (currentClient == null) {
 				return -1L;
 			}
 
 			currentClient.setMicMuted(mute);
-			sessionManager.update(currentClient);
+			streamClientManager.update(currentClient);
 
 			Map<Integer, Object> newMessage = new HashMap<>();
 			newMessage.put(0, "updateMuteStatus");
@@ -690,7 +707,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 		new MessageSender(getChildScope(String.valueOf(roomId)), method, obj, this) {
 			@Override
 			public boolean filter(IConnection conn) {
-				StreamClient rcl = sessionManager.get(IClientUtil.getId(conn.getClient()));
+				StreamClient rcl = streamClientManager.get(IClientUtil.getId(conn.getClient()));
 				return rcl == null || Client.Type.sharing == rcl.getType()
 						|| rcl.getRoomId() == null || !rcl.getRoomId().equals(roomId) || userDao.get(rcl.getUserId()) == null;
 			}
@@ -818,7 +835,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 	public int sendMessageWithClientWithSyncObject(Object newMessage, boolean sync) {
 		try {
 			IConnection current = Red5.getConnectionLocal();
-			StreamClient currentClient = sessionManager.get(IClientUtil.getId(current.getClient()));
+			StreamClient currentClient = streamClientManager.get(IClientUtil.getId(current.getClient()));
 
 			Map<String, Object> hsm = new HashMap<>();
 			hsm.put("client", currentClient);
@@ -908,7 +925,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 	public void startInterviewRecording(Client c) {
 		_log.debug("-----------  startInterviewRecording");
 
-		if (c == null || sessionManager.getRecordingCount(c.getRoom().getId()) > 0) {
+		if (c == null || streamClientManager.getRecordingCount(c.getRoom().getId()) > 0) {
 			return;
 		}
 
@@ -927,7 +944,7 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 
 	public void micActivity(boolean active) {
 		IConnection current = Red5.getConnectionLocal();
-		StreamClient client = sessionManager.get(IClientUtil.getId(current.getClient()));
+		StreamClient client = streamClientManager.get(IClientUtil.getId(current.getClient()));
 		WebSocketHelper.sendRoom(new TextRoomMessage(client.getRoomId(), client, RoomMessage.Type.audioActivity
 				, new JSONObject().put("sid", client.getSid()).put("active", active).toString()));
 	}
@@ -938,10 +955,10 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 	public List<String> listRoomBroadcast() {
 		List<String> ids = new ArrayList<>();
 		IConnection current = Red5.getConnectionLocal();
-		StreamClient client = sessionManager.get(IClientUtil.getId(current.getClient()));
-		for (Client c: getApp().getOmRoomClients(client.getRoomId()) ) {
+		StreamClient client = streamClientManager.get(IClientUtil.getId(current.getClient()));
+		for (Client c: clientManager.listByRoom(client.getRoomId()) ) {
 			for (String uid : c.getStreams()) {
-				StreamClient rc = sessionManager.get(uid);
+				StreamClient rc = streamClientManager.get(uid);
 				ids.add(rc.getBroadcastId());
 			}
 		}
@@ -976,22 +993,22 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 	}
 
 	public List<Long> getActiveRoomIds() {
-		return new ArrayList<>(getApp().getActiveRoomIds());
+		return new ArrayList<>(clientManager.getActiveRoomIds());
 	}
 
 	public synchronized int updateSipTransport() {
 		_log.debug("-----------  updateSipTransport");
 		IConnection current = Red5.getConnectionLocal();
-		StreamClient client = sessionManager.get(IClientUtil.getId(current.getClient()));
+		StreamClient client = streamClientManager.get(IClientUtil.getId(current.getClient()));
 		Long roomId = client.getRoomId();
 		Integer count = getSipConferenceMembersNumber(roomId);
 		String newNumber = getSipTransportLastname(count);
 		_log.debug("getSipConferenceMembersNumber: " + newNumber);
 		if (!newNumber.equals(client.getLastname())) {
-			Client cl = getApp().getOmClientBySid(client.getSid());
+			Client cl = clientManager.getBySid(client.getSid());
 			cl.getUser().setLastname(newNumber);
 			client.setLastname(newNumber);
-			sessionManager.update(client);
+			streamClientManager.update(client);
 			_log.debug("updateSipTransport: {}, {}, {}, {}, {}", new Object[] { client.getUid(), client.getRoomId(),
 					client.getFirstname(), client.getLastname(), client.getAvsettings() });
 			WebSocketHelper.sendRoom(new TextRoomMessage(client.getRoomId(), client, RoomMessage.Type.rightUpdated, client.getUid()));
@@ -1002,8 +1019,8 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 
 	public CheckDto check() {
 		IConnection current = Red5.getConnectionLocal();
-		StreamClient c = sessionManager.get(IClientUtil.getId(current.getClient()));
-		Client cl = getApp().getOmClientBySid(c.getSid());
+		StreamClient c = streamClientManager.get(IClientUtil.getId(current.getClient()));
+		Client cl = clientManager.getBySid(c.getSid());
 		return new CheckDto(cl);
 	}
 
@@ -1012,10 +1029,10 @@ public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter imp
 			return;
 		}
 		IConnection current = Red5.getConnectionLocal();
-		StreamClient c = sessionManager.get(IClientUtil.getId(current.getClient()));
+		StreamClient c = streamClientManager.get(IClientUtil.getId(current.getClient()));
 		if (c == null) {
 			return;
 		}
-		sessionManager.update(c.setWidth(width.intValue()).setHeight(height.intValue()));
+		streamClientManager.update(c.setWidth(width.intValue()).setHeight(height.intValue()));
 	}
 }
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/service/RecordingService.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/service/RecordingService.java
index 675f403..ca93b4e 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/service/RecordingService.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/service/RecordingService.java
@@ -19,7 +19,6 @@
 package org.apache.openmeetings.core.service;
 
 import static org.apache.openmeetings.core.converter.BaseConverter.printMetaInfo;
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.getApp;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
 
 import java.util.Date;
@@ -27,7 +26,6 @@ import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 
-import org.apache.openmeetings.IApplication;
 import org.apache.openmeetings.core.data.record.converter.InterviewConverterTask;
 import org.apache.openmeetings.core.data.record.converter.RecordingConverterTask;
 import org.apache.openmeetings.core.data.record.listener.StreamListener;
@@ -36,7 +34,6 @@ import org.apache.openmeetings.core.util.IClientUtil;
 import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.dao.record.RecordingMetaDataDao;
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.basic.IClient;
@@ -46,6 +43,8 @@ import org.apache.openmeetings.db.entity.record.RecordingMetaData;
 import org.apache.openmeetings.db.entity.record.RecordingMetaData.Status;
 import org.apache.openmeetings.db.entity.room.StreamClient;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.db.manager.IClientManager;
+import org.apache.openmeetings.db.manager.IStreamClientManager;
 import org.apache.openmeetings.db.util.ws.RoomMessage;
 import org.apache.openmeetings.db.util.ws.TextRoomMessage;
 import org.apache.openmeetings.util.CalendarPatterns;
@@ -73,9 +72,10 @@ public class RecordingService {
 	 */
 	private static final Map<Long, StreamListener> streamListeners = new ConcurrentHashMap<>();
 
-	// Spring Beans
 	@Autowired
-	private ISessionManager sessionManager;
+	private IStreamClientManager streamClientManager;
+	@Autowired
+	private IClientManager clientManager;
 	@Autowired
 	private UserDao userDao;
 	@Autowired
@@ -135,13 +135,11 @@ public class RecordingService {
 			// Update Client and set Flag
 			client.setRecordingStarted(true);
 			if (!(client instanceof Client)) {
-				IApplication iapp = getApp();
-				Client c = iapp.getOmClientBySid(client.getSid());
+				Client c = clientManager.getBySid(client.getSid());
 				c.setRecordingId(recordingId);
 				c.setRecordingStarted(true);
-				iapp.update(c);
 			}
-			sessionManager.update(client);
+			streamClientManager.update(client);
 
 			// get all stream and start recording them
 			for (IConnection conn : scope.getClientConnections()) {
@@ -165,9 +163,8 @@ public class RecordingService {
 				log.error("Unable to find recordingId on recording stop");
 				return;
 			}
-			IApplication iapp = getApp();
 			Client recClient = null;
-			for (Client c : iapp.getOmRoomClients(client.getRoomId())) {
+			for (Client c : clientManager.listByRoom(client.getRoomId())) {
 				if (c.getRecordingId() != null) {
 					recClient = c;
 					break;
@@ -182,13 +179,13 @@ public class RecordingService {
 				// Store to database
 				recClient.setRecordingId(null);
 				recClient.setRecordingStarted(false);
-				sessionManager.update(recClient);
+				streamClientManager.update(recClient);
 			}
 			WebSocketHelper.sendRoom(new TextRoomMessage(stopClient.getRoomId(), stopClient, RoomMessage.Type.recordingStoped, stopClient.getSid()));
 			// get all stream and stop recording them
 			for (IConnection conn : scope.getClientConnections()) {
 				if (conn != null && conn instanceof IServiceCapableConnection) {
-					StreamClient rcl = sessionManager.get(IClientUtil.getId(conn.getClient()));
+					StreamClient rcl = streamClientManager.get(IClientUtil.getId(conn.getClient()));
 					stopStreamRecord(scope, rcl);
 				}
 			}
@@ -319,7 +316,7 @@ public class RecordingService {
 
 		// Remove Meta Data
 		rcl.setMetaId(null);
-		sessionManager.update(rcl);
+		streamClientManager.update(rcl);
 	}
 
 	public void startStreamRecord(IConnection conn) {
@@ -331,7 +328,7 @@ public class RecordingService {
 	private void startStreamRecord(IConnection conn, Long recordingId, boolean isInterview) {
 		Date now = new Date();
 
-		StreamClient rcl = sessionManager.get(IClientUtil.getId(conn.getClient()));
+		StreamClient rcl = streamClientManager.get(IClientUtil.getId(conn.getClient()));
 		String broadcastId = rcl.getBroadcastId();
 		if (rcl.getMetaId() != null && streamListeners.get(rcl.getMetaId()) != null) {
 			log.debug("startStreamRecord[{}]:: existing metaId: {}", broadcastId, rcl.getMetaId());
@@ -353,7 +350,7 @@ public class RecordingService {
 
 					// Add Meta Data
 					rcl.setMetaId(metaId);
-					sessionManager.update(rcl);
+					streamClientManager.update(rcl);
 				}
 			} else if ("av".equals(rcl.getAvsettings()) || audioOnly || videoOnly) {
 				// if the user does publish av, a, v
@@ -368,7 +365,7 @@ public class RecordingService {
 
 				// Add Meta Data
 				rcl.setMetaId(metaId);
-				sessionManager.update(rcl);
+				streamClientManager.update(rcl);
 			}
 		}
 		log.debug("startStreamRecord[{}]:: resulting metaId: {}", broadcastId, rcl.getMetaId());
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/session/SessionManager.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/session/SessionManager.java
deleted file mode 100644
index 2fd71a5..0000000
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/session/SessionManager.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-package org.apache.openmeetings.core.session;
-
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.getApp;
-import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
-
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.apache.openmeetings.IApplication;
-import org.apache.openmeetings.db.dao.server.ISessionManager;
-import org.apache.openmeetings.db.entity.basic.Client;
-import org.apache.openmeetings.db.entity.basic.IClient;
-import org.apache.openmeetings.db.entity.room.StreamClient;
-import org.red5.logging.Red5LoggerFactory;
-import org.red5.server.Server;
-import org.slf4j.Logger;
-import org.springframework.stereotype.Component;
-
-/**
- * Handle {@link StreamClient} objects.
- *
- * Use a kind of decorator pattern to inject the {@link Server} into every call.
- *
- * @author sebawagner
- *
- */
-@Component
-public class SessionManager implements ISessionManager {
-	protected static final Logger log = Red5LoggerFactory.getLogger(SessionManager.class, getWebAppRootKey());
-
-	private static Map<String, StreamClient> getClients() {
-		return getApp().getStreamClients();
-	}
-
-	@Override
-	public StreamClient add(StreamClient c) {
-		if (c == null) {
-			return null;
-		}
-		IApplication iapp = getApp();
-		c.setServerId(iapp.getServerId());
-		c.setConnectedSince(new Date());
-		c.setRoomEnter(new Date());
-		iapp.update(c);
-		return c;
-	}
-
-	@Override
-	public Collection<StreamClient> list() {
-		return getClients().values();
-	}
-
-	@Override
-	public StreamClient get(String uid) {
-		if (uid == null) {
-			return null;
-		}
-		return getClients().get(uid);
-	}
-
-	@Override
-	public void update(IClient rcm) {
-		getApp().update(rcm);
-	}
-
-	@Override
-	public boolean remove(String uid) {
-		if (uid == null) {
-			return false;
-		}
-		StreamClient c = getClients().remove(uid);
-		return c != null;
-	}
-
-	@Override
-	public List<StreamClient> listByRoom(Long roomId) {
-		return list().stream()
-				.filter(c -> roomId.equals(c.getRoomId()) && Client.Type.sharing != c.getType())
-				.collect(Collectors.toList());
-	}
-
-	@Override
-	public long getRecordingCount(Long roomId) {
-		if (roomId == null) {
-			return 0;
-		}
-		return list().stream()
-				.filter(c -> roomId.equals(c.getRoomId()) && c.isRecordingStarted())
-				.collect(Collectors.toList()).size();
-	}
-
-	@Override
-	public long getPublishingCount(Long roomId) {
-		if (roomId == null) {
-			return 0;
-		}
-		return list().stream()
-				.filter(c -> roomId.equals(c.getRoomId()) && c.isPublishStarted())
-				.collect(Collectors.toList()).size();
-	}
-
-	@Override
-	public long getSharingCount(Long roomId) {
-		if (roomId == null) {
-			return 0;
-		}
-		return list().stream()
-				.filter(c -> roomId.equals(c.getRoomId()) && c.isSharingStarted())
-				.collect(Collectors.toList()).size();
-	}
-
-	@Override
-	public long getBroadcastingCount(Long roomId) {
-		if (roomId == null) {
-			return 0;
-		}
-		return list().stream()
-				.filter(c -> roomId.equals(c.getRoomId()) && c.isBroadcasting() && c.getBroadcastId() != null)
-				.collect(Collectors.toList()).size();
-	}
-
-	@Override
-	public Set<Long> getActiveRoomIds() {
-		return getApp().getActiveRoomIds();
-	}
-
-	@Override
-	public Set<Long> getActiveRoomIds(String serverId) {
-		Set<Long> ids = new HashSet<>();
-		if (serverId != null) {
-			for (Map.Entry<String, StreamClient> e : getClients().entrySet()) {
-				if (serverId.equals(e.getValue().getServerId())) {
-					ids.add(e.getValue().getRoomId());
-				}
-			}
-		}
-		return ids;
-	}
-}
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java
index 8d91e67..a0ede37 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java
@@ -41,6 +41,7 @@ import org.apache.openmeetings.db.entity.basic.ChatMessage;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.room.Room.Right;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.db.manager.IClientManager;
 import org.apache.openmeetings.db.util.FormatHelper;
 import org.apache.openmeetings.db.util.ws.RoomMessage;
 import org.apache.openmeetings.db.util.ws.TextRoomMessage;
@@ -182,7 +183,7 @@ public class WebSocketHelper {
 		if (publish) {
 			publish(new WsMessageUser(userId, m));
 		}
-		send(a -> ((IApplication)a).getOmClients(userId), (t, c) -> {
+		send(a -> ((IApplication)a).getOmBean(IClientManager.class).listByUser(userId), (t, c) -> {
 			try {
 				t.sendMessage(m);
 			} catch (IOException e) {
@@ -230,7 +231,7 @@ public class WebSocketHelper {
 	}
 
 	private static void sendRoom(final Long roomId, BiConsumer<IWebSocketConnection, Client> consumer, Predicate<Client> check) {
-		send(a -> ((IApplication)a).getOmRoomClients(roomId), consumer, check);
+		send(a -> ((IApplication)a).getOmBean(IClientManager.class).listByRoom(roomId), consumer, check);
 	}
 
 	private static void send(
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/IApplication.java b/openmeetings-db/src/main/java/org/apache/openmeetings/IApplication.java
index 0ae584a..e107dfd 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/IApplication.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/IApplication.java
@@ -18,27 +18,18 @@
  */
 package org.apache.openmeetings;
 
-import java.util.List;
 import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
 import java.util.function.Supplier;
 
 import javax.servlet.ServletContext;
 
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
-import org.apache.openmeetings.db.dto.room.Whiteboards;
-import org.apache.openmeetings.db.entity.basic.Client;
-import org.apache.openmeetings.db.entity.basic.IClient;
 import org.apache.openmeetings.db.entity.room.Invitation;
-import org.apache.openmeetings.db.entity.room.StreamClient;
 import org.apache.openmeetings.util.ws.IClusterWsMessage;
 import org.apache.wicket.request.IExceptionMapper;
 import org.apache.wicket.request.IRequestMapper;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 
-import com.hazelcast.core.IMap;
-
 public interface IApplication {
 	<T> T getOmBean(Class<T> clazz);
 	<T> T _getOmBean(Class<T> clazz);
@@ -51,31 +42,14 @@ public interface IApplication {
 	String getOmContactsLink();
 	String getOmInvitationLink(Invitation i);
 	String urlForActivatePage(PageParameters pp);
-	void invalidateClient(Long userId, String sessionId);
 	void setXFrameOptions(String xFrameOptions);
 	void setContentSecurityPolicy(String contentSecurityPolicy);
 
-	void exit(IClient c);
-	IClient update(IClient c);
-
-	// web client
-	Client getOmClient(String uid);
-	Client getOmClientBySid(String sid);
-	Client getOmOnlineClient(String uid);
-	List<Client> getOmRoomClients(Long roomId);
-	List<Client> getOmClients(Long userId);
-
-	// stream client
-	StreamClient updateClient(StreamClient rcl, boolean forceSize);
 	String getServerId();
-	Map<String, StreamClient> getStreamClients();
-	Set<Long> getActiveRoomIds();
 
 	//JPA
 	void updateJpaAddresses(ConfigurationDao dao);
 
 	//WS
 	void publishWsTopic(IClusterWsMessage msg);
-
-	IMap<Long, Whiteboards> getWhiteboards();
 }
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/SessiondataDao.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/SessiondataDao.java
index e7c800d..b51aae0 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/SessiondataDao.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/SessiondataDao.java
@@ -31,6 +31,7 @@ import javax.persistence.TypedQuery;
 
 import org.apache.openmeetings.db.entity.room.StreamClient;
 import org.apache.openmeetings.db.entity.server.Sessiondata;
+import org.apache.openmeetings.db.manager.IStreamClientManager;
 import org.red5.logging.Red5LoggerFactory;
 import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -50,7 +51,7 @@ public class SessiondataDao {
 	private EntityManager em;
 
 	@Autowired
-	private ISessionManager sessionManager;
+	private IStreamClientManager streamClientManager;
 
 	private static Sessiondata newInstance() {
 		log.debug("startsession :: startsession");
@@ -170,7 +171,7 @@ public class SessiondataDao {
 	 */
 	public void clearSessionByRoomId(Long roomId) {
 		try {
-			for (StreamClient rcl : sessionManager.listByRoom(roomId)) {
+			for (StreamClient rcl : streamClientManager.list(roomId)) {
 				String aux = rcl.getSwfurl();
 
 				int start = aux.indexOf("sid=") + 4;
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
index c73032d..b731dfd 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java
@@ -26,12 +26,12 @@ import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.Room.Right;
 import org.apache.openmeetings.db.entity.room.StreamClient;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.db.manager.IStreamClientManager;
 import org.apache.openmeetings.db.util.RoomHelper;
 import org.apache.wicket.protocol.ws.api.registry.IKey;
 import org.apache.wicket.util.string.Strings;
@@ -409,7 +409,7 @@ public class Client implements IClient {
 		return json;
 	}
 
-	public JSONObject streamJson(String _sid, boolean self, ISessionManager mgr) {
+	public JSONObject streamJson(String _sid, boolean self, IStreamClientManager mgr) {
 		JSONArray _streams = new JSONArray();
 		boolean avFound = false;
 		for (String _uid : streams) {
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
new file mode 100644
index 0000000..cab82c6
--- /dev/null
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.openmeetings.db.manager;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.openmeetings.db.entity.basic.Client;
+import org.apache.openmeetings.db.entity.basic.IClient;
+
+public interface IClientManager {
+	Client get(String uid);
+	Client getBySid(String sid);
+	List<Client> listByRoom(Long roomId);
+	List<Client> listByUser(Long userId);
+	Client update(Client c);
+	void exit(IClient c);
+
+
+	/**
+	 * Get a list of all rooms with users in the system.
+	 *
+	 * @return a set, a roomId can be only one time in this list
+	 */
+	Set<Long> getActiveRoomIds();
+}
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/ISessionManager.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IStreamClientManager.java
similarity index 85%
rename from openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/ISessionManager.java
rename to openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IStreamClientManager.java
index eaec577..301bd6a 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/dao/server/ISessionManager.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IStreamClientManager.java
@@ -16,9 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.openmeetings.db.dao.server;
+package org.apache.openmeetings.db.manager;
 
-import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 
@@ -32,15 +31,19 @@ import org.apache.openmeetings.db.entity.room.StreamClient;
  * @author sebawagner
  *
  */
-public interface ISessionManager {
+public interface IStreamClientManager {
 	StreamClient add(StreamClient c);
 
 	/**
-	 * loads the server into the client (only if database cache is used)
+	 * Get all ClientList Objects of that room and domain This Function is
+	 * needed cause it is invoked internally AFTER the current user has been
+	 * already removed from the ClientList to see if the Room is empty again and
+	 * the PollList can be removed
 	 *
-	 * @return - list of all clients
+	 * @param roomId - id of the room
+	 * @return - list of all clients in the room
 	 */
-	Collection<StreamClient> list();
+	List<StreamClient> list(Long roomId);
 
 	/**
 	 * Get a client by its UID
@@ -55,7 +58,9 @@ public interface ISessionManager {
 	 *
 	 * @param rcm - client to update
 	 */
-	void update(IClient rcm);
+	IClient update(IClient rcm);
+
+	StreamClient update(StreamClient rcl, boolean forceSize);
 
 	/**
 	 * Remove a client from the session store
@@ -66,17 +71,6 @@ public interface ISessionManager {
 	boolean remove(String uid);
 
 	/**
-	 * Get all ClientList Objects of that room and domain This Function is
-	 * needed cause it is invoked internally AFTER the current user has been
-	 * already removed from the ClientList to see if the Room is empty again and
-	 * the PollList can be removed
-	 *
-	 * @param roomId - id of the room
-	 * @return - list of all clients in the room
-	 */
-	List<StreamClient> listByRoom(Long roomId);
-
-	/**
 	 * returns number of users performing recording
 	 *
 	 * @param roomId - id of the room
@@ -109,13 +103,6 @@ public interface ISessionManager {
 	long getBroadcastingCount(Long roomId);
 
 	/**
-	 * Get a list of all rooms with users in the system.
-	 *
-	 * @return a set, a roomId can be only one time in this list
-	 */
-	Set<Long> getActiveRoomIds();
-
-	/**
 	 * Get a list of rooms with users on particular cluster node.
 	 *
 	 * @param serverId - id of the server
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IWhiteboardManager.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IWhiteboardManager.java
new file mode 100644
index 0000000..b321ab0
--- /dev/null
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IWhiteboardManager.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.openmeetings.db.manager;
+
+import org.apache.openmeetings.db.dto.room.Whiteboards;
+
+public interface IWhiteboardManager {
+	Whiteboards get(Long roomId);
+}
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/RoomHelper.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/RoomHelper.java
index db5b50f..47cd8cd 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/RoomHelper.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/util/RoomHelper.java
@@ -18,9 +18,9 @@
  */
 package org.apache.openmeetings.db.util;
 
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.room.StreamClient;
+import org.apache.openmeetings.db.manager.IStreamClientManager;
 
 import com.github.openjson.JSONArray;
 import com.github.openjson.JSONObject;
@@ -28,7 +28,7 @@ import com.github.openjson.JSONObject;
 public class RoomHelper {
 	private RoomHelper() {}
 
-	public static JSONObject videoJson(Client c, boolean self, String sid, ISessionManager mgr, String uid) {
+	public static JSONObject videoJson(Client c, boolean self, String sid, IStreamClientManager mgr, String uid) {
 		StreamClient sc = mgr.get(uid);
 		if (sc == null) {
 			return new JSONObject();
diff --git a/openmeetings-install/src/main/java/org/apache/openmeetings/cli/Admin.java b/openmeetings-install/src/main/java/org/apache/openmeetings/cli/Admin.java
index 0eee165..6d21419 100644
--- a/openmeetings-install/src/main/java/org/apache/openmeetings/cli/Admin.java
+++ b/openmeetings-install/src/main/java/org/apache/openmeetings/cli/Admin.java
@@ -52,7 +52,7 @@ import org.apache.openjpa.lib.log.LogFactoryImpl.LogImpl;
 import org.apache.openmeetings.backup.BackupExport;
 import org.apache.openmeetings.backup.BackupImport;
 import org.apache.openmeetings.backup.ProgressHolder;
-import org.apache.openmeetings.core.ldap.LdapLoginManagement;
+import org.apache.openmeetings.core.ldap.LdapLoginManager;
 import org.apache.openmeetings.core.util.StrongPasswordValidator;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.file.FileItemDao;
@@ -380,7 +380,7 @@ public class Admin {
 			throw new ExitException();
 		}
 		Long domainId = Long.valueOf(cmdl.getOptionValue('d'));
-		getApplicationContext().getBean(LdapLoginManagement.class).importUsers(domainId, cmdl.hasOption("print-only"));
+		getApplicationContext().getBean(LdapLoginManager.class).importUsers(domainId, cmdl.hasOption("print-only"));
 	}
 
 	private void reportUploads(StringBuilder report, boolean cleanup) throws IOException {
diff --git a/openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/CleanupJob.java b/openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/CleanupJob.java
index a858035..33b34b1 100644
--- a/openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/CleanupJob.java
+++ b/openmeetings-service/src/main/java/org/apache/openmeetings/service/quartz/scheduler/CleanupJob.java
@@ -30,13 +30,13 @@ import java.util.Map;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.math.NumberUtils;
-import org.apache.openmeetings.core.data.whiteboard.WhiteboardCache;
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.dao.server.SessiondataDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.dto.room.Whiteboard;
 import org.apache.openmeetings.db.dto.room.Whiteboards;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.db.manager.IStreamClientManager;
+import org.apache.openmeetings.db.manager.IWhiteboardManager;
 import org.red5.logging.Red5LoggerFactory;
 import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -51,9 +51,11 @@ public class CleanupJob extends AbstractJob {
 	@Autowired
 	private SessiondataDao sessionDao;
 	@Autowired
-	private ISessionManager sessionManager;
+	private IStreamClientManager streamClientManager;
 	@Autowired
 	private UserDao userDao;
+	@Autowired
+	private IWhiteboardManager wbManager;
 
 	public void setSessionTimeout(long sessionTimeout) {
 		this.sessionTimeout = sessionTimeout;
@@ -114,7 +116,7 @@ public class CleanupJob extends AbstractJob {
 				Long roomId = null;
 				if (NumberUtils.isCreatable(folder.getName())) {
 					roomId = Long.valueOf(folder.getName());
-					Whiteboards wbList = WhiteboardCache.get(roomId);
+					Whiteboards wbList = wbManager.get(roomId);
 					for (Map.Entry<Long, Whiteboard> e : wbList.getWhiteboards().entrySet()) {
 						if (!e.getValue().isEmpty()) {
 							roomId = null;
@@ -122,7 +124,7 @@ public class CleanupJob extends AbstractJob {
 						}
 					}
 				}
-				if (roomId != null && sessionManager.listByRoom(roomId).isEmpty()) {
+				if (roomId != null && streamClientManager.list(roomId).isEmpty()) {
 					File[] files = folder.listFiles(fi -> fi.isFile() && fi.lastModified() + roomFilesTtl < now);
 					if (files != null && files.length > 0) {
 						log.debug("Room files are too old and no users in the room: " + roomId);
diff --git a/openmeetings-service/src/main/java/org/apache/openmeetings/service/user/UserManager.java b/openmeetings-service/src/main/java/org/apache/openmeetings/service/user/UserManager.java
index b93bbf7..057e364 100644
--- a/openmeetings-service/src/main/java/org/apache/openmeetings/service/user/UserManager.java
+++ b/openmeetings-service/src/main/java/org/apache/openmeetings/service/user/UserManager.java
@@ -46,7 +46,6 @@ import org.apache.commons.codec.binary.Base64;
 import org.apache.openmeetings.core.remote.ScopeApplicationAdapter;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.label.LabelDao;
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.dao.server.SessiondataDao;
 import org.apache.openmeetings.db.dao.user.GroupDao;
 import org.apache.openmeetings.db.dao.user.IUserManager;
@@ -59,6 +58,7 @@ import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.entity.user.User.Right;
 import org.apache.openmeetings.db.entity.user.User.Salutation;
 import org.apache.openmeetings.db.entity.user.User.Type;
+import org.apache.openmeetings.db.manager.IStreamClientManager;
 import org.apache.openmeetings.service.mail.EmailManager;
 import org.apache.openmeetings.util.OmException;
 import org.apache.wicket.util.string.Strings;
@@ -86,11 +86,11 @@ public class UserManager implements IUserManager {
 	@Autowired
 	private UserDao userDao;
 	@Autowired
-	private EmailManager emailManagement;
+	private EmailManager emailManager;
 	@Autowired
 	private ScopeApplicationAdapter scopeAdapter;
 	@Autowired
-	private ISessionManager sessionManager;
+	private IStreamClientManager streamClientManager;
 
 	/**
 	 * Method to register a new User, User will automatically be added to the
@@ -203,7 +203,7 @@ public class UserManager implements IUserManager {
 			if (checkName && checkEmail) {
 				String hash = Strings.isEmpty(activatedHash) ? UUID.randomUUID().toString() : activatedHash;
 				if (sendWelcomeMessage && email.length() != 0) {
-					emailManagement.sendMail(login, email, hash, sendConfirmation, languageId);
+					emailManager.sendMail(login, email, hash, sendConfirmation, languageId);
 				}
 				Address a =  new Address();
 				a.setStreet(street);
@@ -282,7 +282,7 @@ public class UserManager implements IUserManager {
 		try {
 			sessionDao.clearSessionByRoomId(roomId);
 
-			for (StreamClient rcl : sessionManager.listByRoom(roomId)) {
+			for (StreamClient rcl : streamClientManager.list(roomId)) {
 				if (rcl == null) {
 					return true;
 				}
@@ -304,7 +304,7 @@ public class UserManager implements IUserManager {
 	@Override
 	public boolean kickById(String uid) {
 		try {
-			StreamClient rcl = sessionManager.get(uid);
+			StreamClient rcl = streamClientManager.get(uid);
 
 			if (rcl == null) {
 				return true;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
index b01b08b..6fbbbb8 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
@@ -27,14 +27,14 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.dao.user.IUserManager;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.basic.IClient;
 import org.apache.openmeetings.db.entity.room.StreamClient;
 import org.apache.openmeetings.web.admin.AdminBasePanel;
 import org.apache.openmeetings.web.admin.SearchableDataView;
-import org.apache.openmeetings.web.app.Application;
+import org.apache.openmeetings.web.app.ClientManager;
+import org.apache.openmeetings.web.app.StreamClientManager;
 import org.apache.openmeetings.web.common.ConfirmableAjaxBorder;
 import org.apache.openmeetings.web.common.PagedEntityListPanel;
 import org.apache.openmeetings.web.data.SearchableDataProvider;
@@ -59,8 +59,8 @@ public class ConnectionsPanel extends AdminBasePanel {
 
 			private List<IClient> list() {
 				List<IClient> l = new ArrayList<>();
-				l.addAll(getBean(ISessionManager.class).list());
-				l.addAll(Application.getClients());
+				l.addAll(getBean(StreamClientManager.class).list());
+				l.addAll(getBean(ClientManager.class).list());
 				return l;
 			}
 
@@ -93,7 +93,7 @@ public class ConnectionsPanel extends AdminBasePanel {
 							getBean(IUserManager.class).kickById(_c.getUid());
 						} else {
 							Client c = (Client)_c;
-							Application.get().invalidateClient(c.getUserId(), c.getSessionId());
+							getBean(ClientManager.class).invalidate(c.getUserId(), c.getSessionId());
 						}
 						target.add(container, details.setVisible(false));
 					}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
index 6cb680c..a9ad590 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/rooms/RoomForm.java
@@ -22,6 +22,7 @@ import static org.apache.openmeetings.db.util.AuthLevelUtil.hasGroupAdminLevel;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.ATTR_CLASS;
 import static org.apache.openmeetings.web.admin.AdminUserChoiceProvider.PAGE_SIZE;
 import static org.apache.openmeetings.web.app.Application.getBean;
+import static org.apache.openmeetings.web.app.Application.kickUser;
 import static org.apache.openmeetings.web.app.WebSession.getRights;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 
@@ -34,22 +35,21 @@ import java.util.stream.Collectors;
 
 import org.apache.openmeetings.db.dao.file.FileItemDao;
 import org.apache.openmeetings.db.dao.room.RoomDao;
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.dao.user.GroupDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
+import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.file.BaseFileItem;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.Room.RoomElement;
 import org.apache.openmeetings.db.entity.room.RoomFile;
 import org.apache.openmeetings.db.entity.room.RoomGroup;
 import org.apache.openmeetings.db.entity.room.RoomModerator;
-import org.apache.openmeetings.db.entity.room.StreamClient;
 import org.apache.openmeetings.db.entity.user.Address;
 import org.apache.openmeetings.db.entity.user.Group;
 import org.apache.openmeetings.db.entity.user.User;
-import org.apache.openmeetings.service.user.UserManager;
 import org.apache.openmeetings.web.admin.AdminBaseForm;
 import org.apache.openmeetings.web.admin.AdminUserChoiceProvider;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.common.ConfirmableAjaxBorder;
 import org.apache.openmeetings.web.util.RestrictiveChoiceProvider;
 import org.apache.openmeetings.web.util.RoomTypeDropDown;
@@ -93,21 +93,20 @@ public class RoomForm extends AdminBaseForm<Room> {
 	private final WebMarkupContainer moderatorContainer = new WebMarkupContainer("moderatorContainer");
 	private final WebMarkupContainer filesContainer = new WebMarkupContainer("filesContainer");
 	private final WebMarkupContainer clientsContainer = new WebMarkupContainer("clientsContainer");
-	private final ListView<StreamClient> clients = new ListView<StreamClient>("clients", new ArrayList<>()) {
+	private final ListView<Client> clients = new ListView<Client>("clients", new ArrayList<>()) {
 		private static final long serialVersionUID = 1L;
 
 		@Override
-		protected void populateItem(final ListItem<StreamClient> item) {
-			StreamClient client = item.getModelObject();
-			item.add(new Label("clientId", "" + client.getId()))
+		protected void populateItem(final ListItem<Client> item) {
+			Client client = item.getModelObject();
+			item.add(new Label("clientId", "" + client.getUserId()))
 				.add(new Label("clientLogin", "" + client.getLogin()))
 				.add(new ConfirmableAjaxBorder("clientDelete", getString("80"), getString("833")) {
 					private static final long serialVersionUID = 1L;
 
 					@Override
 					protected void onSubmit(AjaxRequestTarget target) {
-						StreamClient c = item.getModelObject();
-						getBean(UserManager.class).kickById(c.getUid());
+						kickUser(item.getModelObject());
 						updateClients(target);
 					}
 				});
@@ -420,7 +419,7 @@ public class RoomForm extends AdminBaseForm<Room> {
 
 	void updateClients(AjaxRequestTarget target) {
 		long roomId = getModelObject().getId() != null ? getModelObject().getId() : 0;
-		final List<StreamClient> clientsInRoom = getBean(ISessionManager.class).listByRoom(roomId);
+		final List<Client> clientsInRoom = getBean(ClientManager.class).listByRoom(roomId);
 		clients.setDefaultModelObject(clientsInRoom);
 		target.add(clientsContainer);
 	}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
index 5d7219e..7571782 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
@@ -18,10 +18,6 @@
  */
 package org.apache.openmeetings.web.app;
 
-import static org.apache.openmeetings.core.util.WebSocketHelper.sendRoom;
-import static org.apache.openmeetings.db.dao.room.SipDao.SIP_FIRST_NAME;
-import static org.apache.openmeetings.db.dao.room.SipDao.SIP_USER_NAME;
-import static org.apache.openmeetings.util.OmFileHelper.SIP_USER_ID;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.HEADER_XFRAME_SAMEORIGIN;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getApplicationName;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getBaseUrl;
@@ -41,41 +37,26 @@ import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
-import java.util.function.Predicate;
 
 import org.apache.directory.api.util.Strings;
 import org.apache.openmeetings.IApplication;
-import org.apache.openmeetings.core.remote.MobileService;
-import org.apache.openmeetings.core.remote.ScopeApplicationAdapter;
 import org.apache.openmeetings.core.service.MainService;
 import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.label.LabelDao;
-import org.apache.openmeetings.db.dao.log.ConferenceLogDao;
-import org.apache.openmeetings.db.dao.room.RoomDao;
-import org.apache.openmeetings.db.dao.server.SessiondataDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
-import org.apache.openmeetings.db.dto.room.Whiteboards;
 import org.apache.openmeetings.db.entity.basic.Client;
-import org.apache.openmeetings.db.entity.basic.Client.Activity;
-import org.apache.openmeetings.db.entity.basic.Client.Pod;
-import org.apache.openmeetings.db.entity.basic.IClient;
-import org.apache.openmeetings.db.entity.log.ConferenceLog;
 import org.apache.openmeetings.db.entity.record.Recording;
 import org.apache.openmeetings.db.entity.room.Invitation;
 import org.apache.openmeetings.db.entity.room.Room;
-import org.apache.openmeetings.db.entity.room.Room.Right;
-import org.apache.openmeetings.db.entity.room.StreamClient;
-import org.apache.openmeetings.db.entity.server.Sessiondata;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.entity.user.User.Type;
 import org.apache.openmeetings.db.util.ws.RoomMessage;
+import org.apache.openmeetings.db.util.ws.TextRoomMessage;
 import org.apache.openmeetings.util.OpenmeetingsVariables;
 import org.apache.openmeetings.util.ws.IClusterWsMessage;
 import org.apache.openmeetings.web.pages.AccessDeniedPage;
@@ -126,7 +107,6 @@ import org.apache.wicket.request.http.WebResponse;
 import org.apache.wicket.request.mapper.info.PageComponentInfo;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.mapper.parameter.PageParametersEncoder;
-import org.apache.wicket.util.collections.ConcurrentHashSet;
 import org.apache.wicket.validation.validator.UrlValidator;
 import org.slf4j.Logger;
 import org.springframework.web.context.WebApplicationContext;
@@ -139,7 +119,6 @@ import org.wicketstuff.datastores.hazelcast.HazelcastDataStore;
 import com.hazelcast.config.XmlConfigBuilder;
 import com.hazelcast.core.Hazelcast;
 import com.hazelcast.core.HazelcastInstance;
-import com.hazelcast.core.IMap;
 import com.hazelcast.core.ITopic;
 import com.hazelcast.core.Member;
 import com.hazelcast.core.MemberAttributeEvent;
@@ -149,13 +128,7 @@ import com.hazelcast.core.MembershipListener;
 public class Application extends AuthenticatedWebApplication implements IApplication {
 	private static final Logger log = getLogger(Application.class, getWebAppRootKey());
 	private static boolean isInstalled;
-	private static final String ONLINE_USERS_KEY = "ONLINE_USERS_KEY";
-	private static final String UID_BY_SID_KEY = "UID_BY_SID_KEY";
 	private static final String INVALID_SESSIONS_KEY = "INVALID_SESSIONS_KEY";
-	private static final String ROOMS_KEY = "ROOMS_KEY";
-	private static final String WBS_KEY = "WBS_KEY";
-	private static final String QUICKPOLL_KEY = "QUICKPOLL_KEY";
-	private static final String STREAM_CLIENT_KEY = "STREAM_CLIENT_KEY";
 	public static final String NAME_ATTR_KEY = "name";
 	//additional maps for faster searching should be created
 	private DashboardContext dashboardContext;
@@ -168,9 +141,9 @@ public class Application extends AuthenticatedWebApplication implements IApplica
 	public static final String HASH_MAPPING = "/hash";
 	public static final String SIGNIN_MAPPING = "/signin";
 	public static final String NOTINIT_MAPPING = "/notinited";
+	private final HazelcastInstance hazelcast = Hazelcast.getOrCreateHazelcastInstance(new XmlConfigBuilder().build());
 	private String xFrameOptions = HEADER_XFRAME_SAMEORIGIN;
 	private String contentSecurityPolicy = OpenmeetingsVariables.HEADER_CSP_SELF;
-	private final HazelcastInstance hazelcast = Hazelcast.getOrCreateHazelcastInstance(new XmlConfigBuilder().build());
 	private ITopic<IClusterWsMessage> hazelWsTopic;
 
 	@Override
@@ -193,18 +166,8 @@ public class Application extends AuthenticatedWebApplication implements IApplica
 			public void memberRemoved(MembershipEvent evt) {
 				//server down, need to remove all online clients, process persistent addresses
 				String serverId = evt.getMember().getStringAttribute(NAME_ATTR_KEY);
-				for (Map.Entry<String, Client> e : getOnlineUsers().entrySet()) {
-					if (serverId.equals(e.getValue().getServerId())) {
-						exit(e.getValue());
-					}
-				}
-				Map<String, StreamClient> streams = getStreamClients();
-				for (Iterator<Map.Entry<String, StreamClient>> iter = streams.entrySet().iterator(); iter.hasNext(); ) {
-					Map.Entry<String, StreamClient> e = iter.next();
-					if (serverId.equals(e.getValue().getServerId())) {
-						iter.remove();
-					}
-				}
+				getBean(ClientManager.class).clean(serverId);
+				getBean(StreamClientManager.class).clean(serverId);
 				updateJpaAddresses(_getBean(ConfigurationDao.class));
 			}
 
@@ -259,7 +222,6 @@ public class Application extends AuthenticatedWebApplication implements IApplica
 				}
 			}
 		});
-
 		super.init();
 
 		// register some widgets
@@ -344,244 +306,18 @@ public class Application extends AuthenticatedWebApplication implements IApplica
 		return get().dashboardContext;
 	}
 
-	private Map<String, Client> getOnlineUsers() {
-		return hazelcast.getMap(ONLINE_USERS_KEY);
+	//package private
+	static HazelcastInstance getHazelcast() {
+		return get().hazelcast;
 	}
 
-	private Map<String, String> getInvalidSessions() {
+	//package private
+	Map<String, String> getInvalidSessions() {
 		return hazelcast.getMap(INVALID_SESSIONS_KEY);
 	}
 
-	private IMap<Long, Set<String>> getRooms() {
-		return hazelcast.getMap(ROOMS_KEY);
-	}
-
-	private Map<String, String> getUidBySid() {
-		return hazelcast.getMap(UID_BY_SID_KEY);
-	}
-
-	@Override
-	public IMap<Long, Whiteboards> getWhiteboards() {
-		return hazelcast.getMap(WBS_KEY);
-	}
-
-	public IMap<Long, Map<Long, Boolean>> getQuickPolls() {
-		return hazelcast.getMap(QUICKPOLL_KEY);
-	}
-
-	@Override
-	public Set<Long> getActiveRoomIds() {
-		return getRooms().keySet();
-	}
-
-	@Override
-	public Map<String, StreamClient> getStreamClients() {
-		return hazelcast.getMap(STREAM_CLIENT_KEY);
-	}
-
-	@Override
-	public IClient update(IClient c) {
-		if (c instanceof StreamClient) {
-			hazelcast.getMap(STREAM_CLIENT_KEY).put(c.getUid(), c);
-		} else {
-			update((Client)c);
-		}
-		return c;
-	}
-
-	public static void addOnlineUser(Client c) {
-		log.debug("Adding online client: {}, room: {}", c.getUid(), c.getRoom());
-		c.setServerId(get().getServerId());
-		get().getOnlineUsers().put(c.getUid(), c);
-		get().getUidBySid().put(c.getSid(), c.getUid());
-	}
-
-	public static void exitRoom(IClient c) {
-		Long roomId = c.getRoomId();
-		removeUserFromRoom(c);
-		if (roomId != null) {
-			sendRoom(new RoomMessage(roomId, c, RoomMessage.Type.roomExit));
-			getBean(ConferenceLogDao.class).add(
-					ConferenceLog.Type.roomLeave
-					, c.getUserId(), "0", roomId
-					, c.getRemoteAddress()
-					, String.valueOf(roomId));
-		}
-	}
-
-	@Override
-	public void exit(IClient c) {
-		if (c != null) {
-			exitRoom(c);
-			log.debug("Removing online client: {}, roomId: {}", c.getUid(), c.getRoomId());
-			get().getOnlineUsers().remove(c.getUid());
-			get().getUidBySid().remove(c.getSid());
-		}
-	}
-
-	private static boolean hasVideo(StreamClient rcl) {
-		return rcl != null && rcl.getAvsettings().contains("v");
-	}
-
-	private static boolean hasVideo(Client c) {
-		return c != null && c.hasActivity(Activity.broadcastV);
-	}
-
-	@Override
-	public StreamClient updateClient(StreamClient rcl, boolean forceSize) {
-		if (rcl == null) {
-			return null;
-		}
-		Client client = getClientBySid(rcl.getSid());
-		if (client == null) {
-			if (Client.Type.mobile == rcl.getType()) {
-				Sessiondata sd = getBean(SessiondataDao.class).check(rcl.getSid());
-				UserDao udao = getBean(UserDao.class);
-				User u = udao.get(sd.getUserId());
-				rcl = getBean(MobileService.class).create(rcl, u);
-				//Mobile client enters the room
-				client = new Client(rcl, udao.get(rcl.getUserId()));
-				addOnlineUser(client);
-				if (rcl.getRoomId() != null) {
-					client.setCam(0);
-					client.setMic(0);
-					client.setRoom(getBean(RoomDao.class).get(rcl.getRoomId()));
-					addUserToRoom(client);
-					WebSocketHelper.sendRoom(new RoomMessage(client.getRoom().getId(), client, RoomMessage.Type.roomEnter));
-				}
-			} else if (client == null && Client.Type.sip == rcl.getType()) {
-				rcl.setLogin(SIP_USER_NAME);
-				rcl.setUserId(SIP_USER_ID);
-				//SipTransport enters the room
-				User u = new User();
-				u.setId(SIP_USER_ID);
-				u.setLogin(SIP_USER_NAME);
-				u.setFirstname(SIP_FIRST_NAME);
-				client = new Client(rcl, u);
-				addOnlineUser(client);
-				client.setCam(0);
-				client.setMic(0);
-				client.allow(Room.Right.audio, Room.Right.video);
-				client.set(Activity.broadcastA);
-				addUserToRoom(client);
-				WebSocketHelper.sendRoom(new RoomMessage(client.getRoom().getId(), client, RoomMessage.Type.roomEnter));
-			} else {
-				return null;
-			}
-		}
-		if (rcl.getRoomId() == null || !rcl.getRoomId().equals(client.getRoom().getId())) {
-			return null;
-		}
-		User u = client.getUser();
-		rcl.setUserId(u.getId());
-		rcl.setLogin(u.getLogin());
-		rcl.setFirstname(u.getFirstname());
-		rcl.setLastname(u.getLastname());
-		rcl.setEmail(u.getAddress() == null ? null : u.getAddress().getEmail());
-		rcl.setSuperMod(client.hasRight(Right.superModerator));
-		rcl.setMod(client.hasRight(Right.moderator));
-		if (client.hasActivity(Activity.broadcastA) && client.getMic() < 0) {
-			client.remove(Activity.broadcastA);
-		}
-		if (client.hasActivity(Activity.broadcastV) && client.getCam() < 0) {
-			client.remove(Activity.broadcastV);
-		}
-		if (client.hasActivity(Activity.broadcastA) || client.hasActivity(Activity.broadcastV)) {
-			if (forceSize || rcl.getWidth() == 0 || rcl.getHeight() == 0) {
-				rcl.setWidth(client.getWidth());
-				rcl.setHeight(client.getHeight());
-			}
-			if (client.getPod() != Pod.none) {
-				rcl.setInterviewPodId(client.getPod() == Pod.left ? 1 : 2);
-			}
-			StringBuilder sb = new StringBuilder();
-			if (client.hasActivity(Activity.broadcastA)) {
-				sb.append('a');
-			}
-			if (client.hasActivity(Activity.broadcastV)) {
-				sb.append('v');
-			}
-			if (!rcl.isBroadcasting() || hasVideo(rcl) != hasVideo(client)) {
-				rcl.setBroadcasting(true);
-			}
-			rcl.setAvsettings(sb.toString());
-		} else {
-			rcl.setAvsettings("n");
-			rcl.setBroadcasting(false);
-		}
-		return rcl;
-	}
-
-	public static Client getOnlineClient(String uid) {
-		return uid == null ? null : get().getOnlineUsers().get(uid);
-	}
-
-	@Override
-	public Client getOmOnlineClient(String uid) {
-		return getOnlineClient(uid);
-	}
-
-	@Override
-	public Client getOmClientBySid(String sid) {
-		return getClientBySid(sid);
-	}
-
-	public static Client getClientBySid(String sid) {
-		if (sid == null) {
-			return null;
-		}
-		String uid = get().getUidBySid().get(sid);
-		return uid == null ? null : get().getOnlineUsers().get(uid);
-	}
-
-	public static boolean isUserOnline(Long userId) {
-		boolean isUserOnline = false;
-		for (Map.Entry<String, Client> e : get().getOnlineUsers().entrySet()) {
-			if (e.getValue().getUserId().equals(userId)) {
-				isUserOnline = true;
-				break;
-			}
-		}
-		return isUserOnline;
-	}
-
-	public static List<Client> getClients() {
-		return new ArrayList<>(get().getOnlineUsers().values());
-	}
-
-	public static List<Client> getClients(Long userId) {
-		List<Client> result =  new ArrayList<>();
-		for (Map.Entry<String, Client> e : get().getOnlineUsers().entrySet()) {
-			if (e.getValue().getUserId().equals(userId)) {
-				result.add(e.getValue());
-				break;
-			}
-		}
-		return result;
-	}
-
-	public static Client getClientByKeys(Long userId, String sessionId) {
-		Client client = null;
-		for (Map.Entry<String, Client> e : get().getOnlineUsers().entrySet()) {
-			Client c = e.getValue();
-			if (c.getUserId().equals(userId) && c.getSessionId().equals(sessionId)) {
-				client = c;
-				break;
-			}
-		}
-		return client;
-	}
-
-	@Override
-	public void invalidateClient(Long userId, String sessionId) {
-		Client client = getClientByKeys(userId, sessionId);
-		if (client != null) {
-			Map<String, String> invalid = getInvalidSessions();
-			if (!invalid.containsKey(client.getSessionId())) {
-				invalid.put(client.getSessionId(), client.getUid());
-				exit(client);
-			}
-		}
+	public static void kickUser(Client client) {
+		WebSocketHelper.sendRoom(new TextRoomMessage(client.getRoom().getId(), client, RoomMessage.Type.kick, client.getUid()));
 	}
 
 	public static boolean isInvaldSession(String sessionId) {
@@ -594,108 +330,6 @@ public class Application extends AuthenticatedWebApplication implements IApplica
 		}
 	}
 
-	public static Client update(Client c) {
-		get().getOnlineUsers().put(c.getUid(), c); // update in storage
-		return c;
-	}
-
-	public static Client addUserToRoom(Client c) {
-		Long roomId = c.getRoom().getId();
-		log.debug("Adding online room client: {}, room: {}", c.getUid(), roomId);
-		IMap<Long, Set<String>> rooms = get().getRooms();
-		rooms.lock(roomId);
-		rooms.putIfAbsent(roomId, new ConcurrentHashSet<String>());
-		Set<String> set = rooms.get(roomId);
-		set.add(c.getUid());
-		rooms.put(roomId, set);
-		rooms.unlock(roomId);
-		update(c);
-		return c;
-	}
-
-	public static IClient removeUserFromRoom(IClient _c) {
-		Long roomId = _c.getRoomId();
-		log.debug("Removing online room client: {}, room: {}", _c.getUid(), roomId);
-		if (roomId != null) {
-			Map<Long, Set<String>> rooms = get().getRooms();
-			Set<String> clients = rooms.get(roomId);
-			if (clients != null) {
-				clients.remove(_c.getUid());
-				rooms.put(roomId, clients);
-			}
-			if (_c instanceof StreamClient) {
-				StreamClient sc = (StreamClient)_c;
-				if (Client.Type.mobile != sc.getType() && Client.Type.sip != sc.getType()) {
-					getBean(ScopeApplicationAdapter.class).roomLeaveByScope(_c, roomId);
-				}
-			}
-			if (_c instanceof Client) {
-				getBean(ScopeApplicationAdapter.class).dropSharing(_c, roomId);
-				Client c = (Client)_c;
-				c.setRoom(null);
-				c.clear();
-				update(c);
-			}
-		}
-		return _c;
-	}
-
-	@Override
-	public List<Client> getOmRoomClients(Long roomId) {
-		return getRoomClients(roomId);
-	}
-
-	@Override
-	public List<Client> getOmClients(Long userId) {
-		return getClients(userId);
-	}
-
-	public static List<Client> getRoomClients(Long roomId) {
-		return getRoomClients(roomId, null);
-	}
-
-	public static List<Client> getRoomClients(Long roomId, Predicate<Client> filter) {
-		List<Client> clients = new ArrayList<>();
-		if (roomId != null) {
-			Set<String> uids = get().getRooms().get(roomId);
-			if (uids != null) {
-				for (String uid : uids) {
-					Client c = getOnlineClient(uid);
-					if (c != null && (filter == null || filter.test(c))) {
-						clients.add(c);
-					}
-				}
-			}
-		}
-		return clients;
-	}
-
-	public static Set<Long> getUserRooms(Long userId) {
-		Set<Long> result = new HashSet<>();
-		for (Entry<Long, Set<String>> me : get().getRooms().entrySet()) {
-			for (String uid : me.getValue()) {
-				Client c = getOnlineClient(uid);
-				if (c != null && c.getUserId().equals(userId)) {
-					result.add(me.getKey());
-				}
-			}
-		}
-		return result;
-	}
-
-	public static boolean isUserInRoom(long roomId, long userId) {
-		Set<String> clients = get().getRooms().get(roomId);
-		if (clients != null) {
-			for (String uid : clients) {
-				Client c = getOnlineClient(uid);
-				if (c != null && c.getUserId().equals(userId)) {
-					return true;
-				}
-			}
-		}
-		return false;
-	}
-
 	public <T> T _getBean(Class<T> clazz) {
 		WebApplicationContext wac = getWebApplicationContext(getServletContext());
 		return wac == null ? null : wac.getBean(clazz);
@@ -843,11 +477,6 @@ public class Application extends AuthenticatedWebApplication implements IApplica
 	}
 
 	@Override
-	public Client getOmClient(String uid) {
-		return getOnlineClient(uid);
-	}
-
-	@Override
 	public void setXFrameOptions(String xFrameOptions) {
 		this.xFrameOptions = xFrameOptions;
 	}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java
new file mode 100644
index 0000000..d3b8e85
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java
@@ -0,0 +1,287 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.openmeetings.web.app;
+
+import static org.apache.openmeetings.core.util.WebSocketHelper.sendRoom;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
+import static org.apache.openmeetings.web.app.Application.getHazelcast;
+import static org.red5.logging.Red5LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.openmeetings.core.remote.ScopeApplicationAdapter;
+import org.apache.openmeetings.db.dao.log.ConferenceLogDao;
+import org.apache.openmeetings.db.entity.basic.Client;
+import org.apache.openmeetings.db.entity.basic.IClient;
+import org.apache.openmeetings.db.entity.log.ConferenceLog;
+import org.apache.openmeetings.db.entity.room.StreamClient;
+import org.apache.openmeetings.db.manager.IClientManager;
+import org.apache.openmeetings.db.util.ws.RoomMessage;
+import org.apache.wicket.util.collections.ConcurrentHashSet;
+import org.red5.server.api.scope.IScope;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.hazelcast.core.HazelcastInstance;
+import com.hazelcast.core.IMap;
+
+@Component
+public class ClientManager implements IClientManager {
+	private static final Logger log = getLogger(ClientManager.class, getWebAppRootKey());
+	private static final String ROOMS_KEY = "ROOMS_KEY";
+	private static final String ONLINE_USERS_KEY = "ONLINE_USERS_KEY";
+	private static final String UID_BY_SID_KEY = "UID_BY_SID_KEY";
+	private HazelcastInstance hazelcast;
+
+	@Autowired
+	private ConferenceLogDao confLogDao;
+	@Autowired
+	private ScopeApplicationAdapter scopeAdapter;
+
+	@PostConstruct
+	private void init() {
+		this.hazelcast = getHazelcast();
+	}
+
+	private Map<String, Client> map() {
+		return hazelcast.getMap(ONLINE_USERS_KEY);
+	}
+
+	private Map<String, String> mapUidBySid() {
+		return hazelcast.getMap(UID_BY_SID_KEY);
+	}
+
+	private IMap<Long, Set<String>> getRooms() {
+		return hazelcast.getMap(ROOMS_KEY);
+	}
+
+	public void add(Client c) {
+		log.debug("Adding online client: {}, room: {}", c.getUid(), c.getRoom());
+		c.setServerId(Application.get().getServerId());
+		map().put(c.getUid(), c);
+		mapUidBySid().put(c.getSid(), c.getUid());
+	}
+
+	@Override
+	public Client update(Client c) {
+		map().put(c.getUid(), c); // update in storage
+		return c;
+	}
+	@Override
+	public Client get(String uid) {
+		return uid == null ? null : map().get(uid);
+	}
+
+	@Override
+	public Client getBySid(String sid) {
+		if (sid == null) {
+			return null;
+		}
+		String uid = mapUidBySid().get(sid);
+		return uid == null ? null : map().get(uid);
+	}
+
+	public void exitRoom(IClient c) {
+		Long roomId = c.getRoomId();
+		removeFromRoom(c);
+		if (roomId != null) {
+			sendRoom(new RoomMessage(roomId, c, RoomMessage.Type.roomExit));
+			confLogDao.add(
+					ConferenceLog.Type.roomLeave
+					, c.getUserId(), "0", roomId
+					, c.getRemoteAddress()
+					, String.valueOf(roomId));
+		}
+	}
+
+	@Override
+	public void exit(IClient c) {
+		if (c != null) {
+			exitRoom(c);
+			log.debug("Removing online client: {}, roomId: {}", c.getUid(), c.getRoomId());
+			map().remove(c.getUid());
+			mapUidBySid().remove(c.getSid());
+		}
+	}
+
+	public void clean(String serverId) {
+		Map<String, Client> clients = map();
+		for (Map.Entry<String, Client> e : clients.entrySet()) {
+			if (serverId.equals(e.getValue().getServerId())) {
+				exit(e.getValue());
+			}
+		}
+	}
+
+	@Override
+	public Set<Long> getActiveRoomIds() {
+		return getRooms().keySet();
+	}
+
+	public Client addToRoom(Client c) {
+		Long roomId = c.getRoom().getId();
+		log.debug("Adding online room client: {}, room: {}", c.getUid(), roomId);
+		IMap<Long, Set<String>> rooms = getRooms();
+		rooms.lock(roomId);
+		rooms.putIfAbsent(roomId, new ConcurrentHashSet<String>());
+		Set<String> set = rooms.get(roomId);
+		set.add(c.getUid());
+		rooms.put(roomId, set);
+		rooms.unlock(roomId);
+		update(c);
+		return c;
+	}
+
+	public IClient removeFromRoom(IClient _c) {
+		Long roomId = _c.getRoomId();
+		log.debug("Removing online room client: {}, room: {}", _c.getUid(), roomId);
+		if (roomId != null) {
+			Map<Long, Set<String>> rooms = getRooms();
+			Set<String> clients = rooms.get(roomId);
+			if (clients != null) {
+				clients.remove(_c.getUid());
+				rooms.put(roomId, clients);
+			}
+			if (_c instanceof StreamClient) {
+				StreamClient sc = (StreamClient)_c;
+				if (Client.Type.mobile != sc.getType() && Client.Type.sip != sc.getType()) {
+					scopeAdapter.roomLeaveByScope(_c, roomId);
+				}
+			}
+			if (_c instanceof Client) {
+				scopeAdapter.dropSharing(_c, roomId);
+				Client c = (Client)_c;
+				IScope sc = scopeAdapter.getChildScope(roomId);
+				for (String uid : c.getStreams()) {
+					scopeAdapter.sendMessageById("quit", uid, sc);
+				}
+				c.setRoom(null);
+				c.clear();
+				update(c);
+			}
+		}
+		return _c;
+	}
+
+	public boolean isOnline(Long userId) {
+		boolean isUserOnline = false;
+		for (Map.Entry<String, Client> e : map().entrySet()) {
+			if (e.getValue().getUserId().equals(userId)) {
+				isUserOnline = true;
+				break;
+			}
+		}
+		return isUserOnline;
+	}
+
+	public List<Client> list() {
+		return new ArrayList<>(map().values());
+	}
+
+	@Override
+	public List<Client> listByUser(Long userId) {
+		List<Client> result =  new ArrayList<>();
+		for (Map.Entry<String, Client> e : map().entrySet()) {
+			if (e.getValue().getUserId().equals(userId)) {
+				result.add(e.getValue());
+				break;
+			}
+		}
+		return result;
+	}
+
+	@Override
+	public List<Client> listByRoom(Long roomId) {
+		return listByRoom(roomId, null);
+	}
+
+	public List<Client> listByRoom(Long roomId, Predicate<Client> filter) {
+		List<Client> clients = new ArrayList<>();
+		if (roomId != null) {
+			Set<String> uids = getRooms().get(roomId);
+			if (uids != null) {
+				for (String uid : uids) {
+					Client c = get(uid);
+					if (c != null && (filter == null || filter.test(c))) {
+						clients.add(c);
+					}
+				}
+			}
+		}
+		return clients;
+	}
+
+	public Set<Long> listRoomIds(Long userId) {
+		Set<Long> result = new HashSet<>();
+		for (Entry<Long, Set<String>> me : getRooms().entrySet()) {
+			for (String uid : me.getValue()) {
+				Client c = get(uid);
+				if (c != null && c.getUserId().equals(userId)) {
+					result.add(me.getKey());
+				}
+			}
+		}
+		return result;
+	}
+
+	public boolean isInRoom(long roomId, long userId) {
+		Set<String> clients = getRooms().get(roomId);
+		if (clients != null) {
+			for (String uid : clients) {
+				Client c = get(uid);
+				if (c != null && c.getUserId().equals(userId)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	private Client getByKeys(Long userId, String sessionId) {
+		Client client = null;
+		for (Map.Entry<String, Client> e : map().entrySet()) {
+			Client c = e.getValue();
+			if (c.getUserId().equals(userId) && c.getSessionId().equals(sessionId)) {
+				client = c;
+				break;
+			}
+		}
+		return client;
+	}
+
+	public void invalidate(Long userId, String sessionId) {
+		Client client = getByKeys(userId, sessionId);
+		if (client != null) {
+			Map<String, String> invalid = Application.get().getInvalidSessions();
+			if (!invalid.containsKey(client.getSessionId())) {
+				invalid.put(client.getSessionId(), client.getUid());
+				exit(client);
+			}
+		}
+	}
+}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/StreamClientManager.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/StreamClientManager.java
new file mode 100644
index 0000000..74ad459
--- /dev/null
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/StreamClientManager.java
@@ -0,0 +1,303 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+package org.apache.openmeetings.web.app;
+
+import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.getApp;
+import static org.apache.openmeetings.db.dao.room.SipDao.SIP_FIRST_NAME;
+import static org.apache.openmeetings.db.dao.room.SipDao.SIP_USER_NAME;
+import static org.apache.openmeetings.util.OmFileHelper.SIP_USER_ID;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
+import static org.apache.openmeetings.web.app.Application.getHazelcast;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.openmeetings.IApplication;
+import org.apache.openmeetings.core.remote.MobileService;
+import org.apache.openmeetings.core.util.WebSocketHelper;
+import org.apache.openmeetings.db.dao.room.RoomDao;
+import org.apache.openmeetings.db.dao.server.SessiondataDao;
+import org.apache.openmeetings.db.dao.user.UserDao;
+import org.apache.openmeetings.db.entity.basic.Client;
+import org.apache.openmeetings.db.entity.basic.Client.Activity;
+import org.apache.openmeetings.db.entity.basic.Client.Pod;
+import org.apache.openmeetings.db.entity.basic.IClient;
+import org.apache.openmeetings.db.entity.room.Room;
+import org.apache.openmeetings.db.entity.room.Room.Right;
+import org.apache.openmeetings.db.entity.room.StreamClient;
+import org.apache.openmeetings.db.entity.server.Sessiondata;
+import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.db.manager.IStreamClientManager;
+import org.apache.openmeetings.db.util.ws.RoomMessage;
+import org.red5.logging.Red5LoggerFactory;
+import org.red5.server.Server;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.hazelcast.core.HazelcastInstance;
+
+/**
+ * Handle {@link StreamClient} objects.
+ *
+ * Use a kind of decorator pattern to inject the {@link Server} into every call.
+ *
+ * @author sebawagner
+ *
+ */
+@Component
+public class StreamClientManager implements IStreamClientManager {
+	protected static final Logger log = Red5LoggerFactory.getLogger(StreamClientManager.class, getWebAppRootKey());
+	private static final String STREAM_CLIENT_KEY = "STREAM_CLIENT_KEY";
+	private HazelcastInstance hazelcast;
+
+	@Autowired
+	private ClientManager clientManager;
+	@Autowired
+	private SessiondataDao sessionDao;
+	@Autowired
+	private UserDao userDao;
+	@Autowired
+	private MobileService mobileService;
+	@Autowired
+	private RoomDao roomDao;
+
+	@PostConstruct
+	private void init() {
+		this.hazelcast = getHazelcast();
+	}
+
+	public Map<String, StreamClient> map() {
+		return hazelcast.getMap(STREAM_CLIENT_KEY);
+	}
+
+	@Override
+	public StreamClient add(StreamClient c) {
+		if (c == null) {
+			return null;
+		}
+		IApplication iapp = getApp();
+		c.setServerId(iapp.getServerId());
+		c.setConnectedSince(new Date());
+		c.setRoomEnter(new Date());
+		update(c);
+		return c;
+	}
+
+	public Collection<StreamClient> list() {
+		return map().values();
+	}
+
+	@Override
+	public StreamClient get(String uid) {
+		if (uid == null) {
+			return null;
+		}
+		return map().get(uid);
+	}
+
+	@Override
+	public IClient update(IClient c) {
+		if (c instanceof StreamClient) {
+			hazelcast.getMap(STREAM_CLIENT_KEY).put(c.getUid(), c);
+		} else {
+			clientManager.update((Client)c);
+		}
+		return c;
+	}
+
+	private static boolean hasVideo(StreamClient rcl) {
+		return rcl != null && rcl.getAvsettings().contains("v");
+	}
+
+	private static boolean hasVideo(Client c) {
+		return c != null && c.hasActivity(Activity.broadcastV);
+	}
+
+	@Override
+	public StreamClient update(StreamClient rcl, boolean forceSize) {
+		if (rcl == null) {
+			return null;
+		}
+		Client client = clientManager.getBySid(rcl.getSid());
+		if (client == null) {
+			if (Client.Type.mobile == rcl.getType()) {
+				Sessiondata sd = sessionDao.check(rcl.getSid());
+				User u = userDao.get(sd.getUserId());
+				rcl = mobileService.create(rcl, u);
+				//Mobile client enters the room
+				client = new Client(rcl, userDao.get(rcl.getUserId()));
+				clientManager.add(client);
+				if (rcl.getRoomId() != null) {
+					client.setCam(0);
+					client.setMic(0);
+					client.setRoom(roomDao.get(rcl.getRoomId()));
+					clientManager.add(client);
+					WebSocketHelper.sendRoom(new RoomMessage(client.getRoom().getId(), client, RoomMessage.Type.roomEnter));
+				}
+			} else if (client == null && Client.Type.sip == rcl.getType()) {
+				rcl.setLogin(SIP_USER_NAME);
+				rcl.setUserId(SIP_USER_ID);
+				//SipTransport enters the room
+				User u = new User();
+				u.setId(SIP_USER_ID);
+				u.setLogin(SIP_USER_NAME);
+				u.setFirstname(SIP_FIRST_NAME);
+				client = new Client(rcl, u);
+				clientManager.add(client);
+				client.setCam(0);
+				client.setMic(0);
+				client.allow(Room.Right.audio, Room.Right.video);
+				client.set(Activity.broadcastA);
+				clientManager.addToRoom(client);
+				WebSocketHelper.sendRoom(new RoomMessage(client.getRoom().getId(), client, RoomMessage.Type.roomEnter));
+			} else {
+				return null;
+			}
+		}
+		if (rcl.getRoomId() == null || !rcl.getRoomId().equals(client.getRoom().getId())) {
+			return null;
+		}
+		User u = client.getUser();
+		rcl.setUserId(u.getId());
+		rcl.setLogin(u.getLogin());
+		rcl.setFirstname(u.getFirstname());
+		rcl.setLastname(u.getLastname());
+		rcl.setEmail(u.getAddress() == null ? null : u.getAddress().getEmail());
+		rcl.setSuperMod(client.hasRight(Right.superModerator));
+		rcl.setMod(client.hasRight(Right.moderator));
+		if (client.hasActivity(Activity.broadcastA) && client.getMic() < 0) {
+			client.remove(Activity.broadcastA);
+		}
+		if (client.hasActivity(Activity.broadcastV) && client.getCam() < 0) {
+			client.remove(Activity.broadcastV);
+		}
+		if (client.hasActivity(Activity.broadcastA) || client.hasActivity(Activity.broadcastV)) {
+			if (forceSize || rcl.getWidth() == 0 || rcl.getHeight() == 0) {
+				rcl.setWidth(client.getWidth());
+				rcl.setHeight(client.getHeight());
+			}
+			if (client.getPod() != Pod.none) {
+				rcl.setInterviewPodId(client.getPod() == Pod.left ? 1 : 2);
+			}
+			StringBuilder sb = new StringBuilder();
+			if (client.hasActivity(Activity.broadcastA)) {
+				sb.append('a');
+			}
+			if (client.hasActivity(Activity.broadcastV)) {
+				sb.append('v');
+			}
+			if (!rcl.isBroadcasting() || hasVideo(rcl) != hasVideo(client)) {
+				rcl.setBroadcasting(true);
+			}
+			rcl.setAvsettings(sb.toString());
+		} else {
+			rcl.setAvsettings("n");
+			rcl.setBroadcasting(false);
+		}
+		return rcl;
+	}
+
+	@Override
+	public boolean remove(String uid) {
+		if (uid == null) {
+			return false;
+		}
+		StreamClient c = map().remove(uid);
+		return c != null;
+	}
+
+	@Override
+	public List<StreamClient> list(Long roomId) {
+		return list().stream()
+				.filter(c -> roomId.equals(c.getRoomId()) && Client.Type.sharing != c.getType())
+				.collect(Collectors.toList());
+	}
+
+	@Override
+	public long getRecordingCount(Long roomId) {
+		if (roomId == null) {
+			return 0;
+		}
+		return list().stream()
+				.filter(c -> roomId.equals(c.getRoomId()) && c.isRecordingStarted())
+				.collect(Collectors.toList()).size();
+	}
+
+	@Override
+	public long getPublishingCount(Long roomId) {
+		if (roomId == null) {
+			return 0;
+		}
+		return list().stream()
+				.filter(c -> roomId.equals(c.getRoomId()) && c.isPublishStarted())
+				.collect(Collectors.toList()).size();
+	}
+
+	@Override
+	public long getSharingCount(Long roomId) {
+		if (roomId == null) {
+			return 0;
+		}
+		return list().stream()
+				.filter(c -> roomId.equals(c.getRoomId()) && c.isSharingStarted())
+				.collect(Collectors.toList()).size();
+	}
+
+	@Override
+	public long getBroadcastingCount(Long roomId) {
+		if (roomId == null) {
+			return 0;
+		}
+		return list().stream()
+				.filter(c -> roomId.equals(c.getRoomId()) && c.isBroadcasting() && c.getBroadcastId() != null)
+				.collect(Collectors.toList()).size();
+	}
+
+	@Override
+	public Set<Long> getActiveRoomIds(String serverId) {
+		Set<Long> ids = new HashSet<>();
+		if (serverId != null) {
+			for (Map.Entry<String, StreamClient> e : map().entrySet()) {
+				if (serverId.equals(e.getValue().getServerId())) {
+					ids.add(e.getValue().getRoomId());
+				}
+			}
+		}
+		return ids;
+	}
+
+	public void clean(String serverId) {
+		Map<String, StreamClient> streams = map();
+		for (Iterator<Map.Entry<String, StreamClient>> iter = streams.entrySet().iterator(); iter.hasNext(); ) {
+			Map.Entry<String, StreamClient> e = iter.next();
+			if (serverId.equals(e.getValue().getServerId())) {
+				iter.remove();
+			}
+		}
+	}
+}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/WebSession.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/WebSession.java
index 9483562..b2c7386 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/WebSession.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/WebSession.java
@@ -45,7 +45,7 @@ import java.util.TimeZone;
 
 import org.apache.commons.lang3.time.FastDateFormat;
 import org.apache.openmeetings.IWebSession;
-import org.apache.openmeetings.core.ldap.LdapLoginManagement;
+import org.apache.openmeetings.core.ldap.LdapLoginManager;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.room.InvitationDao;
 import org.apache.openmeetings.db.dao.server.SOAPLoginDao;
@@ -118,7 +118,7 @@ public class WebSession extends AbstractAuthenticatedWebSession implements IWebS
 
 	@Override
 	public void invalidate() {
-		Application.get().invalidateClient(userId, getId());
+		getBean(ClientManager.class).invalidate(userId, getId());
 		super.invalidate();
 		userId = null;
 		rights = Collections.unmodifiableSet(Collections.<Right>emptySet());
@@ -312,7 +312,7 @@ public class WebSession extends AbstractAuthenticatedWebSession implements IWebS
 		User u;
 		switch (type) {
 			case ldap:
-				u = getBean(LdapLoginManagement.class).login(login, password, domainId);
+				u = getBean(LdapLoginManager.class).login(login, password, domainId);
 				break;
 			case user:
 				/* we will allow login against internal DB in case user 'guess' LDAP password */
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/whiteboard/WhiteboardCache.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/WhiteboardManager.java
similarity index 68%
rename from openmeetings-core/src/main/java/org/apache/openmeetings/core/data/whiteboard/WhiteboardCache.java
rename to openmeetings-web/src/main/java/org/apache/openmeetings/web/app/WhiteboardManager.java
index 9bba020..de0b351 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/whiteboard/WhiteboardCache.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/WhiteboardManager.java
@@ -16,38 +16,50 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.openmeetings.core.data.whiteboard;
+package org.apache.openmeetings.web.app;
 
-import static org.apache.openmeetings.core.remote.ScopeApplicationAdapter.getApp;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getDefaultLang;
+import static org.apache.openmeetings.web.app.Application.getHazelcast;
 
 import java.util.Map.Entry;
 import java.util.Set;
 
+import javax.annotation.PostConstruct;
+
 import org.apache.openmeetings.db.dao.label.LabelDao;
 import org.apache.openmeetings.db.dto.room.Whiteboard;
 import org.apache.openmeetings.db.dto.room.Whiteboards;
+import org.apache.openmeetings.db.manager.IWhiteboardManager;
+import org.springframework.stereotype.Component;
 
+import com.hazelcast.core.HazelcastInstance;
 import com.hazelcast.core.IMap;
 
 /**
- * Memory based cache, configured as singleton in spring configuration
+ * Hazelcast based Whiteboard manager
  *
  * @author sebawagner
  *
  */
-public class WhiteboardCache {
-	private WhiteboardCache() {}
+@Component
+public class WhiteboardManager implements IWhiteboardManager {
+	private static final String WBS_KEY = "WBS_KEY";
+	private HazelcastInstance hazelcast;
+
+	@PostConstruct
+	private void init() {
+		this.hazelcast = getHazelcast();
+	}
 
-	private static IMap<Long, Whiteboards> getCache() {
-		return getApp().getWhiteboards();
+	private IMap<Long, Whiteboards> getCache() {
+		return hazelcast.getMap(WBS_KEY);
 	}
 
-	public static boolean tryLock(Long roomId) {
+	public boolean tryLock(Long roomId) {
 		return getCache().tryLock(roomId);
 	}
 
-	public static void unlock(Long roomId) {
+	public void unlock(Long roomId) {
 		getCache().unlock(roomId);
 	}
 
@@ -59,15 +71,16 @@ public class WhiteboardCache {
 		return sb.toString();
 	}
 
-	public static boolean contains(Long roomId) {
+	public boolean contains(Long roomId) {
 		return getCache().containsKey(roomId);
 	}
 
-	public static Whiteboards get(Long roomId) {
+	@Override
+	public Whiteboards get(Long roomId) {
 		return get(roomId, null);
 	}
 
-	public static Whiteboards get(Long roomId, Long langId) {
+	public Whiteboards get(Long roomId, Long langId) {
 		if (roomId == null) {
 			return null;
 		}
@@ -81,12 +94,12 @@ public class WhiteboardCache {
 		return wbs;
 	}
 
-	public static Set<Entry<Long, Whiteboard>> list(long roomId) {
+	public Set<Entry<Long, Whiteboard>> list(long roomId) {
 		Whiteboards wbs = get(roomId);
 		return wbs.getWhiteboards().entrySet();
 	}
 
-	public static Whiteboard add(long roomId, Long langId) {
+	public Whiteboard add(long roomId, Long langId) {
 		Whiteboards wbs = get(roomId);
 		Whiteboard wb = add(wbs, langId);
 		update(wbs);
@@ -99,7 +112,7 @@ public class WhiteboardCache {
 		return wb;
 	}
 
-	public static Whiteboard clear(long roomId, Long wbId) {
+	public Whiteboard clear(long roomId, Long wbId) {
 		Whiteboards wbs = get(roomId);
 		Whiteboard wb = wbs.get(wbId);
 		if (wb != null) {
@@ -109,26 +122,26 @@ public class WhiteboardCache {
 		return wb;
 	}
 
-	public static Whiteboard remove(long roomId, Long wbId) {
+	public Whiteboard remove(long roomId, Long wbId) {
 		Whiteboards wbs = get(roomId);
 		Whiteboard wb = wbs.getWhiteboards().remove(wbId);
 		update(wbs);
 		return wb;
 	}
 
-	public static void activate(long roomId, Long wbId) {
+	public void activate(long roomId, Long wbId) {
 		Whiteboards wbs = get(roomId);
 		wbs.setActiveWb(wbId);
 		update(wbs);
 	}
 
-	public static void update(long roomId, Whiteboard wb) {
+	public void update(long roomId, Whiteboard wb) {
 		Whiteboards wbs = get(roomId);
 		wbs.update(wb);
 		update(wbs);
 	}
 
-	private static void update(Whiteboards wbs) {
+	private void update(Whiteboards wbs) {
 		getCache().put(wbs.getRoomId(), wbs);
 	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java
index 3fb2554..10eb391 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java
@@ -23,9 +23,7 @@ import static org.apache.openmeetings.db.util.AuthLevelUtil.hasGroupAdminLevel;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_MYROOMS_ENABLED;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_USER_ID;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
-import static org.apache.openmeetings.web.app.Application.addOnlineUser;
 import static org.apache.openmeetings.web.app.Application.getBean;
-import static org.apache.openmeetings.web.app.Application.getOnlineClient;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import static org.apache.openmeetings.web.util.CallbackFunctionHelper.getNamedFunction;
 import static org.apache.openmeetings.web.util.CallbackFunctionHelper.getParam;
@@ -48,6 +46,7 @@ import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.user.PrivateMessage;
 import org.apache.openmeetings.db.entity.user.User.Right;
 import org.apache.openmeetings.web.app.Application;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.common.menu.MainMenuItem;
 import org.apache.openmeetings.web.common.menu.MenuPanel;
@@ -139,7 +138,7 @@ public class MainPanel extends Panel {
 				ExtendedClientProperties cp = WebSession.get().getExtendedProperties();
 				final Client client = new Client(getSession().getId(), msg.getKey().hashCode(), getUserId(), getBean(UserDao.class));
 				uid = client.getUid();
-				addOnlineUser(cp.update(client));
+				getBean(ClientManager.class).add(cp.update(client));
 				log.debug("WebSocketBehavior::onConnect [uid: {}, session: {}, key: {}]", client.getUid(), msg.getSessionId(), msg.getKey());
 			}
 
@@ -176,7 +175,7 @@ public class MainPanel extends Panel {
 				log.debug("WebSocketBehavior::closeHandler [uid: {}, session: {}, key: {}]", uid, msg.getSessionId(), msg.getKey());
 				//no chance to stop pingTimer here :(
 				if (uid != null) {
-					Application.get().exit(getClient());
+					getBean(ClientManager.class).exit(getClient());
 					uid = null;
 				}
 			}
@@ -436,6 +435,6 @@ public class MainPanel extends Panel {
 	}
 
 	public Client getClient() {
-		return getOnlineClient(uid);
+		return getBean(ClientManager.class).get(uid);
 	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/NicknameDialog.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/NicknameDialog.java
index 518cc46..25e5f1c 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/NicknameDialog.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/NicknameDialog.java
@@ -18,7 +18,7 @@
  */
 package org.apache.openmeetings.web.room;
 
-import static org.apache.openmeetings.web.app.Application.update;
+import static org.apache.openmeetings.web.app.Application.getBean;
 import static org.apache.wicket.validation.validator.StringValidator.minimumLength;
 
 import java.util.Arrays;
@@ -28,6 +28,7 @@ import java.util.List;
 import org.apache.commons.lang3.time.FastDateFormat;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.util.NonClosableDialog;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.extensions.validation.validator.RfcCompliantEmailAddressValidator;
@@ -110,6 +111,6 @@ public class NicknameDialog extends NonClosableDialog<User> {
 		final User u = form.getModelObject();
 		final Client c = room.getClient();
 		c.getUser().setFirstname(u.getFirstname()).setLastname(u.getLastname());
-		room.broadcast(update(c));
+		room.broadcast(getBean(ClientManager.class).update(c));
 	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomBroadcaster.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomBroadcaster.java
index 7d0faae..2d2a3e4 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomBroadcaster.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomBroadcaster.java
@@ -20,13 +20,12 @@ package org.apache.openmeetings.web.room;
 
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
 import static org.apache.openmeetings.web.app.Application.getBean;
-import static org.apache.openmeetings.web.app.Application.update;
 
 import org.apache.openmeetings.core.remote.ScopeApplicationAdapter;
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.room.StreamClient;
-import org.apache.openmeetings.web.app.Application;
+import org.apache.openmeetings.web.app.ClientManager;
+import org.apache.openmeetings.web.app.StreamClientManager;
 import org.red5.logging.Red5LoggerFactory;
 import org.slf4j.Logger;
 
@@ -36,7 +35,7 @@ public class RoomBroadcaster {
 	private RoomBroadcaster() {}
 
 	public static StreamClient getClient(String publicSid) {
-		return getBean(ISessionManager.class).get(publicSid);
+		return getBean(StreamClientManager.class).get(publicSid);
 	}
 
 	public static void broadcast(String publicSid, String method, Object obj) {
@@ -54,10 +53,10 @@ public class RoomBroadcaster {
 
 	public static void sendUpdatedClient(Client client) {
 		String uid = client.getUid();
-		StreamClient rcl = Application.get().updateClient(getClient(uid), true);
+		StreamClient rcl = getBean(StreamClientManager.class).update(getClient(uid), true);
 		log.debug("-----------  sendUpdatedClient ");
 		// Notify all clients of the same scope (room)
-		update(client);
+		getBean(ClientManager.class).update(client);
 		broadcast(client.getRoom().getId(), "clientUpdated", rcl);
 
 		if (rcl == null) {
@@ -65,6 +64,6 @@ public class RoomBroadcaster {
 		}
 
 		// Put the mod-flag to true for this client
-		getBean(ISessionManager.class).update(rcl);
+		getBean(StreamClientManager.class).update(rcl);
 	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
index f25ae26..ce0d9a4 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
@@ -21,13 +21,7 @@ package org.apache.openmeetings.web.room;
 import static org.apache.openmeetings.db.util.RoomHelper.videoJson;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.ATTR_CLASS;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
-import static org.apache.openmeetings.web.app.Application.addUserToRoom;
-import static org.apache.openmeetings.web.app.Application.exitRoom;
 import static org.apache.openmeetings.web.app.Application.getBean;
-import static org.apache.openmeetings.web.app.Application.getClientBySid;
-import static org.apache.openmeetings.web.app.Application.getOnlineClient;
-import static org.apache.openmeetings.web.app.Application.getRoomClients;
-import static org.apache.openmeetings.web.app.Application.update;
 import static org.apache.openmeetings.web.app.WebSession.getDateFormat;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import static org.apache.openmeetings.web.room.wb.InterviewWbPanel.INTERVIEWWB_JS_REFERENCE;
@@ -45,7 +39,6 @@ import java.util.Set;
 import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.dao.calendar.AppointmentDao;
 import org.apache.openmeetings.db.dao.log.ConferenceLogDao;
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.calendar.Appointment;
@@ -64,7 +57,8 @@ import org.apache.openmeetings.db.util.ws.RoomMessage;
 import org.apache.openmeetings.db.util.ws.RoomMessage.Type;
 import org.apache.openmeetings.db.util.ws.TextRoomMessage;
 import org.apache.openmeetings.util.NullStringer;
-import org.apache.openmeetings.web.app.Application;
+import org.apache.openmeetings.web.app.ClientManager;
+import org.apache.openmeetings.web.app.StreamClientManager;
 import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.common.BasePanel;
 import org.apache.openmeetings.web.pages.BasePage;
@@ -155,7 +149,7 @@ public class RoomPanel extends BasePanel {
 				sidebar.setFilesActive(target);
 			}
 			if (Room.Type.presentation != r.getType()) {
-				List<Client> mods = Application.getRoomClients(r.getId(), c -> c.hasRight(Room.Right.moderator));
+				List<Client> mods = getBean(ClientManager.class).listByRoom(r.getId(), c -> c.hasRight(Room.Right.moderator));
 				if (mods.isEmpty()) {
 					waitApplyModeration.open(target);
 				}
@@ -167,10 +161,10 @@ public class RoomPanel extends BasePanel {
 			StringBuilder sb = new StringBuilder();
 			boolean hasStreams = false;
 			Client _c = getClient();
-			for (Client c: getRoomClients(getRoom().getId()) ) {
+			for (Client c: getBean(ClientManager.class).listByRoom(getRoom().getId())) {
 				boolean self = _c.getUid().equals(c.getUid());
 				for (String uid : c.getStreams()) {
-					JSONObject jo = videoJson(c, self, c.getSid(), getBean(ISessionManager.class), uid);
+					JSONObject jo = videoJson(c, self, c.getSid(), getBean(StreamClientManager.class), uid);
 					sb.append(String.format("VideoManager.play(%s);", jo));
 					hasStreams = true;
 				}
@@ -243,7 +237,8 @@ public class RoomPanel extends BasePanel {
 	protected void onInitialize() {
 		super.onInitialize();
 		//let's refresh user in client
-		update(getClient().updateUser(getBean(UserDao.class)));
+		ClientManager cm = getBean(ClientManager.class);
+		cm.update(getClient().updateUser(getBean(UserDao.class)));
 		Component accessDenied = new WebMarkupContainer(ACCESS_DENIED_ID).setVisible(false);
 
 		room.add(AttributeModifier.append(ATTR_CLASS, r.getType().name()));
@@ -284,7 +279,7 @@ public class RoomPanel extends BasePanel {
 		add(roomClosed = new RedirectMessageDialog("room-closed", "1098", r.isClosed(), r.getRedirectURL()));
 		if (r.isClosed()) {
 			room.setVisible(false);
-		} else if (getRoomClients(r.getId()).size() >= r.getCapacity()) {
+		} else if (cm.listByRoom(r.getId()).size() >= r.getCapacity()) {
 			accessDenied = new ExpiredMessageDialog(ACCESS_DENIED_ID, getString("99"), menu);
 			room.setVisible(false);
 		} else if (r.getId().equals(WebSession.get().getRoomId())) {
@@ -401,6 +396,7 @@ public class RoomPanel extends BasePanel {
 			if (wsEvent.getMessage() instanceof RoomMessage) {
 				RoomMessage m = (RoomMessage)wsEvent.getMessage();
 				IPartialPageRequestHandler handler = wsEvent.getHandler();
+				ClientManager cm = getBean(ClientManager.class);
 				switch (m.getType()) {
 					case pollCreated:
 						menu.updatePoll(handler, m.getUserId());
@@ -411,13 +407,13 @@ public class RoomPanel extends BasePanel {
 					case recordingStoped:
 						{
 							String uid = ((TextRoomMessage)m).getText();
-							Client c = getClientBySid(uid);
+							Client c = cm.getBySid(uid);
 							if (c == null) {
 								log.error("Not existing/BAD user has stopped recording {} != {} !!!!", uid);
 								return;
 							}
 							recordingUser = null;
-							update(c.remove(Client.Activity.record));
+							cm.update(c.remove(Client.Activity.record));
 							menu.update(handler);
 							updateInterviewRecordingButtons(handler);
 						}
@@ -426,13 +422,13 @@ public class RoomPanel extends BasePanel {
 						{
 							JSONObject obj = new JSONObject(((TextRoomMessage)m).getText());
 							String sid = obj.getString("sid");
-							Client c = getClientBySid(sid);
+							Client c = cm.getBySid(sid);
 							if (c == null) {
 								log.error("Not existing user has started recording {} !!!!", sid);
 								return;
 							}
 							recordingUser = sid;
-							update(c.set(Client.Activity.record));
+							cm.update(c.set(Client.Activity.record));
 							menu.update(handler);
 							updateInterviewRecordingButtons(handler);
 						}
@@ -441,34 +437,34 @@ public class RoomPanel extends BasePanel {
 						{
 							JSONObject obj = new JSONObject(((TextRoomMessage)m).getText());
 							String uid = obj.getString("uid");
-							Client c = getClientBySid(obj.getString("sid"));
+							Client c = cm.getBySid(obj.getString("sid"));
 							if (c == null) {
 								log.error("Not existing user has started sharing {} !!!!", obj);
 								return;
 							}
 							handler.appendJavaScript(String.format("VideoManager.close('%s', true);", uid));
 							sharingUser = null;
-							update(c.removeStream(uid).remove(Client.Activity.share));
+							cm.update(c.removeStream(uid).remove(Client.Activity.share));
 							menu.update(handler);
 						}
 						break;
 					case sharingStarted:
 						{
 							String uid = ((TextRoomMessage)m).getText();
-							Client c = getOnlineClient(uid);
+							Client c = cm.get(uid);
 							if (c == null) {
 								log.error("Not existing user has started sharing {} !!!!", uid);
 								return;
 							}
 							sharingUser = uid;
-							update(c.set(Client.Activity.share));
+							cm.update(c.set(Client.Activity.share));
 							menu.update(handler);
 						}
 						break;
 					case rightUpdated:
 						{
 							String uid = ((TextRoomMessage)m).getText();
-							Client c = getOnlineClient(uid);
+							Client c = cm.get(uid);
 							if (c == null) {
 								log.error("Not existing user in rightUpdated {} !!!!", uid);
 								return;
@@ -476,7 +472,7 @@ public class RoomPanel extends BasePanel {
 							Client _c = getClient();
 							boolean self = _c.getUid().equals(c.getUid());
 							handler.appendJavaScript(String.format("VideoManager.update(%s);"
-									, c.streamJson(_c.getSid(), self, getBean(ISessionManager.class)).toString(new NullStringer())
+									, c.streamJson(_c.getSid(), self, getBean(StreamClientManager.class)).toString(new NullStringer())
 									));
 							sidebar.update(handler);
 							menu.update(handler);
@@ -488,10 +484,10 @@ public class RoomPanel extends BasePanel {
 					{
 						JSONObject obj = new JSONObject(((TextRoomMessage)m).getText());
 						String uid = obj.getString("uid");
-						Client c = getOnlineClient(uid);
+						Client c = cm.get(uid);
 						if (c == null) {
 							// screen client, ext video stream
-							c = getClientBySid(obj.getString("sid"));
+							c = cm.getBySid(obj.getString("sid"));
 						}
 						if (c == null) {
 							log.error("Not existing user in newStream {} !!!!", uid);
@@ -499,13 +495,13 @@ public class RoomPanel extends BasePanel {
 						}
 						Client _c = getClient();
 						boolean self = _c.getSid().equals(c.getSid());
-						ISessionManager mgr = getBean(ISessionManager.class);
+						StreamClientManager mgr = getBean(StreamClientManager.class);
 						if (!self || Client.Type.room != mgr.get(uid).getType()) { // stream from others or self external video
 							JSONObject jo = videoJson(c, false, _c.getSid(), mgr, uid);
 							handler.appendJavaScript(String.format("VideoManager.play(%s);", jo));
 						}
 						if (self) {
-							update(c.addStream(uid));
+							cm.update(c.addStream(uid));
 						}
 						updateInterviewRecordingButtons(handler);
 					}
@@ -514,12 +510,12 @@ public class RoomPanel extends BasePanel {
 					{
 						JSONObject obj = new JSONObject(((TextRoomMessage)m).getText());
 						String uid = obj.getString("uid");
-						Client c = getClientBySid(obj.getString("sid"));
+						Client c = cm.getBySid(obj.getString("sid"));
 						if (c != null) {
 							//c == null means client exits the room
 							Client _c = getClient();
 							if (_c.getUid().equals(c.getUid())) {
-								update(c.removeStream(uid));
+								cm.update(c.removeStream(uid));
 							}
 						}
 						handler.appendJavaScript(String.format("VideoManager.close('%s');", uid));
@@ -581,14 +577,14 @@ public class RoomPanel extends BasePanel {
 								handler.add(room.setVisible(false));
 								getMainPanel().getChat().toggle(handler, false);
 								clientKicked.open(handler);
-								exitRoom(getClient());
+								cm.exitRoom(getClient());
 							}
 						}
 						break;
 					case audioActivity:
 					{
 						JSONObject obj = new JSONObject(((TextRoomMessage)m).getText());
-						Client c = getClientBySid(obj.getString("sid"));
+						Client c = cm.getBySid(obj.getString("sid"));
 						if (c == null) {
 							log.error("Not existing user in audioActivity {} !!!!", obj);
 							return;
@@ -601,7 +597,7 @@ public class RoomPanel extends BasePanel {
 					case mute:
 					{
 						JSONObject obj = new JSONObject(((TextRoomMessage)m).getText());
-						Client c = getClientBySid(obj.getString("sid"));
+						Client c = cm.getBySid(obj.getString("sid"));
 						if (c == null) {
 							log.error("Not existing user in mute {} !!!!", obj);
 							return;
@@ -614,7 +610,7 @@ public class RoomPanel extends BasePanel {
 					case exclusive:
 					{
 						String uid = ((TextRoomMessage)m).getText();
-						Client c = getOnlineClient(uid);
+						Client c = cm.get(uid);
 						if (c == null) {
 							// no luck
 							return;
@@ -633,7 +629,7 @@ public class RoomPanel extends BasePanel {
 		if (interview && _c.hasRight(Right.moderator)) {
 			if (recordingUser == null) {
 				boolean hasStreams = false;
-				for (Client cl : getRoomClients(r.getId())) {
+				for (Client cl : getBean(ClientManager.class).listByRoom(r.getId())) {
 					if (!cl.getStreams().isEmpty()) {
 						hasStreams = true;
 						break;
@@ -652,16 +648,17 @@ public class RoomPanel extends BasePanel {
 		if (room.isVisible()) {
 			//We are setting initial rights here
 			Client c = getClient();
-			addUserToRoom(c.setRoom(getRoom()));
+			ClientManager cm = getBean(ClientManager.class);
+			cm.addToRoom(c.setRoom(getRoom()));
 			SOAPLogin soap = WebSession.get().getSoapLogin();
 			if (soap != null && soap.isModerator()) {
 				c.allow(Right.superModerator);
-				update(c);
+				cm.update(c);
 			} else {
-				Set<Right> rr = AuthLevelUtil.getRoomRight(c.getUser(), r, r.isAppointment() ? getBean(AppointmentDao.class).getByRoom(r.getId()) : null, getRoomClients(r.getId()).size());
+				Set<Right> rr = AuthLevelUtil.getRoomRight(c.getUser(), r, r.isAppointment() ? getBean(AppointmentDao.class).getByRoom(r.getId()) : null, cm.listByRoom(r.getId()).size());
 				if (!rr.isEmpty()) {
 					c.allow(rr);
-					update(c);
+					cm.update(c);
 				}
 			}
 		}
@@ -672,7 +669,7 @@ public class RoomPanel extends BasePanel {
 	}
 
 	public static boolean hasRight(long userId, long roomId, Right r) {
-		for (Client c : getRoomClients(roomId)) {
+		for (Client c : getBean(ClientManager.class).listByRoom(roomId)) {
 			if (c.getUserId().equals(userId) && c.hasRight(r)) {
 				return true;
 			}
@@ -715,7 +712,7 @@ public class RoomPanel extends BasePanel {
 			getMainPanel().getChat().toggle(handler, true);
 		}
 		handler.appendJavaScript("if (typeof(Room) !== 'undefined') { Room.unload(); }");
-		Application.exitRoom(getClient());
+		getBean(ClientManager.class).exitRoom(getClient());
 		getMainPanel().getChat().roomExit(r, handler);
 	}
 
@@ -736,7 +733,8 @@ public class RoomPanel extends BasePanel {
 
 	public void requestRight(Right right, IPartialPageRequestHandler handler) {
 		RoomMessage.Type reqType = null;
-		List<Client> mods = Application.getRoomClients(r.getId(), c -> c.hasRight(Room.Right.moderator));
+		ClientManager cm = getBean(ClientManager.class);
+		List<Client> mods = cm.listByRoom(r.getId(), c -> c.hasRight(Room.Right.moderator));
 		if (mods.isEmpty()) {
 			if (r.isModerated()) {
 				//dialog
@@ -744,7 +742,7 @@ public class RoomPanel extends BasePanel {
 				return;
 			} else {
 				// we found no-one we can ask, allow right
-				broadcast(update(getClient().allow(right)));
+				broadcast(getBean(ClientManager.class).update(getClient().allow(right)));
 			}
 		}
 		// ask
@@ -786,7 +784,7 @@ public class RoomPanel extends BasePanel {
 
 	public void allowRight(Client client, Right... rights) {
 		client.allow(rights);
-		update(client);
+		getBean(ClientManager.class).update(client);
 		broadcast(client);
 	}
 
@@ -800,7 +798,7 @@ public class RoomPanel extends BasePanel {
 		if (client.hasActivity(Client.Activity.broadcastV) && !client.hasRight(Right.video)) {
 			client.remove(Client.Activity.broadcastV);
 		}
-		update(client);
+		getBean(ClientManager.class).update(client);
 		broadcast(client);
 	}
 
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomResourceReference.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomResourceReference.java
index ae84cce..01e54bb 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomResourceReference.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomResourceReference.java
@@ -26,21 +26,21 @@ import static org.apache.openmeetings.util.OmFileHelper.MP4_MIME_TYPE;
 import static org.apache.openmeetings.util.OmFileHelper.PNG_MIME_TYPE;
 import static org.apache.openmeetings.util.OmFileHelper.getOmHome;
 import static org.apache.openmeetings.web.app.Application.getBean;
-import static org.apache.openmeetings.web.app.Application.getOnlineClient;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 
 import java.io.File;
 import java.util.Map.Entry;
 
 import org.apache.directory.api.util.Strings;
-import org.apache.openmeetings.core.data.whiteboard.WhiteboardCache;
 import org.apache.openmeetings.db.dao.file.FileItemDao;
 import org.apache.openmeetings.db.dao.user.GroupUserDao;
 import org.apache.openmeetings.db.dto.room.Whiteboard;
 import org.apache.openmeetings.db.dto.room.Whiteboards;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.file.FileItem;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.app.WebSession;
+import org.apache.openmeetings.web.app.WhiteboardManager;
 import org.apache.openmeetings.web.util.FileItemResourceReference;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.IResource.Attributes;
@@ -94,7 +94,7 @@ public class RoomResourceReference extends FileItemResourceReference<FileItem> {
 			//no-op expected
 		}
 		WebSession ws = WebSession.get();
-		Client c = getOnlineClient(uid);
+		Client c = getBean(ClientManager.class).get(uid);
 		if (id == null || !ws.isSignedIn() || c == null) {
 			return null;
 		}
@@ -105,7 +105,7 @@ public class RoomResourceReference extends FileItemResourceReference<FileItem> {
 		String ruid = params.get("ruid").toString();
 		String wuid = params.get("wuid").toString();
 		if (c.getRoom() != null) {
-			Whiteboards wbs = WhiteboardCache.get(c.getRoom().getId());
+			Whiteboards wbs = getBean(WhiteboardManager.class).get(c.getRoom().getId());
 			if (!Strings.isEmpty(wuid) && !Strings.isEmpty(ruid) && ruid.equals(wbs.getUid())) {
 				for (Entry<Long, Whiteboard> e : wbs.getWhiteboards().entrySet()) {
 					JSONObject file = e.getValue().get(wuid);
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java
index 76e3875..69892d5 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/VideoSettings.java
@@ -34,10 +34,10 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.openmeetings.db.dao.room.RoomDao;
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.util.OmFileHelper;
 import org.apache.openmeetings.util.OpenmeetingsVariables;
 import org.apache.openmeetings.web.app.Application;
+import org.apache.openmeetings.web.app.StreamClientManager;
 import org.apache.openmeetings.web.util.ExtendedClientProperties;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.JavaScriptHeaderItem;
@@ -110,7 +110,7 @@ public class VideoSettings extends Panel {
 		if (servers.size() > 1) {
 			for (Member m : servers) {
 				String serverId = m.getStringAttribute(NAME_ATTR_KEY);
-				Set<Long> roomIds = getBean(ISessionManager.class).getActiveRoomIds(serverId);
+				Set<Long> roomIds = getBean(StreamClientManager.class).getActiveRoomIds(serverId);
 				if (roomIds.contains(roomId)) {
 					// if the room is already opened on a server, redirect the user to that one,
 					log.debug("Room is already opened on a server {}", m.getAddress());
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/activities/ActivitiesPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/activities/ActivitiesPanel.java
index 88d52d9..4f8a3f8 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/activities/ActivitiesPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/activities/ActivitiesPanel.java
@@ -20,7 +20,7 @@ package org.apache.openmeetings.web.room.activities;
 
 import static org.apache.openmeetings.core.util.WebSocketHelper.sendRoom;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
-import static org.apache.openmeetings.web.app.Application.getOnlineClient;
+import static org.apache.openmeetings.web.app.Application.getBean;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import static org.apache.openmeetings.web.util.CallbackFunctionHelper.getNamedFunction;
 import static org.apache.wicket.ajax.attributes.CallbackParameter.explicit;
@@ -34,6 +34,7 @@ import org.apache.openmeetings.db.entity.room.Room.Right;
 import org.apache.openmeetings.db.entity.room.Room.RoomElement;
 import org.apache.openmeetings.db.util.ws.RoomMessage;
 import org.apache.openmeetings.db.util.ws.TextRoomMessage;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.pages.BasePage;
 import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.wicket.Component;
@@ -95,7 +96,7 @@ public class ActivitiesPanel extends Panel {
 						}
 						break;
 					case accept:
-						Client client = getOnlineClient(a.getUid());
+						Client client = getBean(ClientManager.class).get(a.getUid());
 						if (room.getClient().hasRight(Right.moderator) && client != null && client.getRoom() != null && roomId == client.getRoom().getId()) {
 							switch (a.getType()) {
 								case reqRightModerator:
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java
index 6aab765..ac717ee 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java
@@ -26,9 +26,7 @@ import static org.apache.openmeetings.util.OpenmeetingsVariables.ATTR_TITLE;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_REDIRECT_URL_FOR_EXTERNAL;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getBaseUrl;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.isSipEnabled;
-import static org.apache.openmeetings.web.app.Application.exitRoom;
 import static org.apache.openmeetings.web.app.Application.getBean;
-import static org.apache.openmeetings.web.app.Application.getClientBySid;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import static org.apache.openmeetings.web.util.GroupLogoResourceReference.getUrl;
 import static org.apache.openmeetings.web.util.OmUrlFragment.ROOMS_PUBLIC;
@@ -48,6 +46,7 @@ import org.apache.openmeetings.db.entity.user.Group;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.util.ws.RoomMessage.Type;
 import org.apache.openmeetings.db.util.ws.TextRoomMessage;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.common.ImagePanel;
 import org.apache.openmeetings.web.common.InvitationDialog;
@@ -370,13 +369,14 @@ public class RoomMenuPanel extends Panel {
 		StringBuilder roomClass = new StringBuilder("room name");
 		StringBuilder roomTitle = new StringBuilder();
 		if (room.getRecordingUser() != null) {
-			Client recClient = getClientBySid(room.getRecordingUser());
+			ClientManager cm = getBean(ClientManager.class);
+			Client recClient = cm.getBySid(room.getRecordingUser());
 			if (recClient != null) {
 				roomTitle.append(String.format("%s %s %s %s %s", getString("419")
 						, recClient.getUser().getLogin(), recClient.getUser().getFirstname(), recClient.getUser().getLastname(), df.format(recClient.getConnectedSince())));
 				roomClass.append(" screen");
 			}
-			Client pubClient = getClientBySid(room.getPublishingUser());
+			Client pubClient = cm.getBySid(room.getPublishingUser());
 			if (pubClient != null) {
 				if (recClient != null) {
 					roomTitle.append('\n');
@@ -408,7 +408,7 @@ public class RoomMenuPanel extends Panel {
 	}
 
 	public void exit(IPartialPageRequestHandler handler) {
-		exitRoom(room.getClient());
+		getBean(ClientManager.class).exitRoom(room.getClient());
 		if (WebSession.getRights().contains(User.Right.Dashboard)) {
 			room.getMainPanel().updateContents(ROOMS_PUBLIC, handler);
 		} else {
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/StartSharingButton.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/StartSharingButton.java
index eddb908..d558989 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/StartSharingButton.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/StartSharingButton.java
@@ -28,7 +28,6 @@ import static org.apache.openmeetings.util.OpenmeetingsVariables.FLASH_NATIVE_SS
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getApplicationName;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
 import static org.apache.openmeetings.web.app.Application.getBean;
-import static org.apache.openmeetings.web.app.Application.getOnlineClient;
 import static org.apache.openmeetings.web.app.WebSession.getLanguage;
 import static org.apache.wicket.util.time.Duration.NONE;
 
@@ -38,9 +37,10 @@ import org.apache.commons.io.IOUtils;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.label.LabelDao;
 import org.apache.openmeetings.db.dao.room.RoomDao;
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.room.Room;
+import org.apache.openmeetings.web.app.ClientManager;
+import org.apache.openmeetings.web.app.StreamClientManager;
 import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.common.OmButton;
 import org.apache.openmeetings.web.room.VideoSettings;
@@ -78,7 +78,7 @@ public class StartSharingButton extends OmButton {
 
 			@Override
 			protected IResourceStream getResourceStream(Attributes attributes) {
-				setFileName(String.format("public_%s.jnlp", getOnlineClient(uid).getRoom().getId()));
+				setFileName(String.format("public_%s.jnlp", getBean(ClientManager.class).get(uid).getRoom().getId()));
 				StringResourceStream srs = new StringResourceStream(app, "application/x-java-jnlp-file");
 				srs.setCharset(UTF_8);
 				return srs;
@@ -97,13 +97,13 @@ public class StartSharingButton extends OmButton {
 		try (InputStream jnlp = getClass().getClassLoader().getResourceAsStream("APPLICATION.jnlp")) {
 			ConfigurationDao cfgDao = getBean(ConfigurationDao.class);
 			app = IOUtils.toString(jnlp, UTF_8);
-			Client c = getOnlineClient(uid);
+			Client c = getBean(ClientManager.class).get(uid);
 			String sid = c.getSid();
 			Long roomId = c.getRoom().getId();
 			JSONObject s = VideoSettings.getInitJson(WebSession.get().getExtendedProperties(), roomId, sid);
 			String _url = s.getString(VideoSettings.URL);
 			Room room = getBean(RoomDao.class).get(roomId);
-			ISessionManager sessionManager = getBean(ISessionManager.class);
+			StreamClientManager streamClientManager = getBean(StreamClientManager.class);
 			app = app.replace("$native", String.valueOf(s.getBoolean(FLASH_NATIVE_SSL)))
 					.replace("$codebase", WebSession.get().getExtendedProperties().getCodebase())
 					.replace("$applicationName", getApplicationName())
@@ -122,8 +122,8 @@ public class StartSharingButton extends OmButton {
 					.replace("$defaultFps", String.valueOf(cfgDao.getLong(CONFIG_SCREENSHARING_FPS, 10L)))
 					.replace("$showFps", String.valueOf(cfgDao.getBool(CONFIG_SCREENSHARING_FPS_SHOW, true)))
 					.replace("$allowRemote", String.valueOf(cfgDao.getBool(CONFIG_SCREENSHARING_ALLOW_REMOTE, true)))
-					.replace("$allowRecording", String.valueOf(room.isAllowRecording() && (0 == sessionManager.getRecordingCount(roomId))))
-					.replace("$allowPublishing", String.valueOf(0 == sessionManager.getPublishingCount(roomId)))
+					.replace("$allowRecording", String.valueOf(room.isAllowRecording() && (0 == streamClientManager.getRecordingCount(roomId))))
+					.replace("$allowPublishing", String.valueOf(0 == streamClientManager.getPublishingCount(roomId)))
 					;
 			download.initiate(target);
 		} catch (Exception e) {
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
index 6eed1f9..8d4680d 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomSidebar.java
@@ -19,8 +19,7 @@
 package org.apache.openmeetings.web.room.sidebar;
 
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
-import static org.apache.openmeetings.web.app.Application.getOnlineClient;
-import static org.apache.openmeetings.web.app.Application.getRoomClients;
+import static org.apache.openmeetings.web.app.Application.getBean;
 import static org.apache.openmeetings.web.room.RoomBroadcaster.sendUpdatedClient;
 import static org.apache.openmeetings.web.util.CallbackFunctionHelper.getNamedFunction;
 import static org.apache.wicket.ajax.attributes.CallbackParameter.explicit;
@@ -35,6 +34,7 @@ import org.apache.openmeetings.db.entity.room.Room.Right;
 import org.apache.openmeetings.db.entity.room.Room.RoomElement;
 import org.apache.openmeetings.db.util.ws.RoomMessage;
 import org.apache.openmeetings.db.util.ws.TextRoomMessage;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.common.ConfirmableAjaxBorder;
 import org.apache.openmeetings.web.common.ConfirmableAjaxBorder.ConfirmableBorderDialog;
@@ -106,12 +106,13 @@ public class RoomSidebar extends Panel {
 				if (Strings.isEmpty(uid)) {
 					return;
 				}
+				ClientManager cm = getBean(ClientManager.class);
 				Client cl = room.getClient();
 				Action a = Action.valueOf(getRequest().getRequestParameters().getParameterValue(PARAM_ACTION).toString());
 				switch (a) {
 					case kick:
 						if (cl.hasRight(Right.moderator)) {
-							kickedClient = getOnlineClient(uid);
+							kickedClient = cm.get(uid);
 							if (kickedClient == null) {
 								return;
 							}
@@ -128,7 +129,7 @@ public class RoomSidebar extends Panel {
 					case mute:
 					{
 						JSONObject obj = uid.isEmpty() ? new JSONObject() : new JSONObject(uid);
-						Client _c = getOnlineClient(obj.getString("uid"));
+						Client _c = cm.get(obj.getString("uid"));
 						if (_c == null || !_c.hasActivity(Client.Activity.broadcastA)) {
 							return;
 						}
@@ -159,7 +160,7 @@ public class RoomSidebar extends Panel {
 				}
 				Right right = Right.valueOf(getRequest().getRequestParameters().getParameterValue(PARAM_RIGHT).toString());
 				if (room.getClient().hasRight(Right.moderator)) {
-					Client client = getOnlineClient(uid);
+					Client client = getBean(ClientManager.class).get(uid);
 					if (client == null) {
 						return;
 					}
@@ -197,7 +198,7 @@ public class RoomSidebar extends Panel {
 				Client.Activity a = Client.Activity.valueOf(getRequest().getRequestParameters().getParameterValue(PARAM_ACTIVITY).toString());
 				StringValue podStr = getRequest().getRequestParameters().getParameterValue(PARAM_POD);
 				Pod pod = podStr.isEmpty() ? Pod.none : Pod.valueOf(podStr.toString());
-				Client c = getOnlineClient(uid);
+				Client c = getBean(ClientManager.class).get(uid);
 				toggleActivity(c, a, pod);
 			} catch (Exception e) {
 				log.error("Unexpected exception while toggle 'activity'", e);
@@ -275,7 +276,7 @@ public class RoomSidebar extends Panel {
 	}
 
 	private ListView<Client> updateUsers() {
-		users.setList(getRoomClients(room.getRoom().getId()));
+		users.setList(getBean(ClientManager.class).listByRoom(room.getRoom().getId()));
 		return users;
 	}
 
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/icon/ClientIcon.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/icon/ClientIcon.java
index 3c7f7ec..0dd3191 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/icon/ClientIcon.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/icon/ClientIcon.java
@@ -20,12 +20,13 @@ package org.apache.openmeetings.web.room.sidebar.icon;
 
 import static org.apache.openmeetings.util.OpenmeetingsVariables.ATTR_CLASS;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.ATTR_TITLE;
-import static org.apache.openmeetings.web.app.Application.getOnlineClient;
+import static org.apache.openmeetings.web.app.Application.getBean;
 import static org.apache.openmeetings.web.pages.BasePage.ALIGN_LEFT;
 import static org.apache.openmeetings.web.pages.BasePage.ALIGN_RIGHT;
 import static org.apache.openmeetings.web.util.CallbackFunctionHelper.addOnClick;
 
 import org.apache.openmeetings.db.entity.basic.Client;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.pages.BasePage;
 import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.wicket.AttributeModifier;
@@ -90,6 +91,6 @@ public abstract class ClientIcon extends WebMarkupContainer {
 	}
 
 	protected Client getClient() {
-		return getOnlineClient(uid);
+		return getBean(ClientManager.class).get(uid);
 	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/InterviewWbPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/InterviewWbPanel.java
index 1aa379e..32f9f87 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/InterviewWbPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/InterviewWbPanel.java
@@ -23,11 +23,11 @@ import static org.apache.openmeetings.web.app.Application.getBean;
 import java.io.IOException;
 
 import org.apache.openmeetings.core.remote.ScopeApplicationAdapter;
-import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.file.BaseFileItem;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.Room.Right;
+import org.apache.openmeetings.web.app.StreamClientManager;
 import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.request.resource.JavaScriptResourceReference;
@@ -59,12 +59,12 @@ public class InterviewWbPanel extends AbstractWbPanel {
 		if (c.hasRight(Room.Right.moderator)) {
 			switch (a) {
 				case startRecording:
-					if (getBean(ISessionManager.class).getRecordingCount(c.getRoomId()) < 1) {
+					if (getBean(StreamClientManager.class).getRecordingCount(c.getRoomId()) < 1) {
 						getBean(ScopeApplicationAdapter.class).startInterviewRecording(c);
 					}
 					break;
 				case stopRecording:
-					if (getBean(ISessionManager.class).getRecordingCount(c.getRoomId()) < 1) {
+					if (getBean(StreamClientManager.class).getRecordingCount(c.getRoomId()) < 1) {
 						getBean(ScopeApplicationAdapter.class).stopInterviewRecording(c);
 					}
 					break;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
index ac69890..0265c5d 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbPanel.java
@@ -56,7 +56,6 @@ import javax.imageio.ImageIO;
 
 import org.apache.commons.codec.binary.Base64;
 import org.apache.directory.api.util.Strings;
-import org.apache.openmeetings.core.data.whiteboard.WhiteboardCache;
 import org.apache.openmeetings.db.dao.file.FileItemDao;
 import org.apache.openmeetings.db.dto.room.Whiteboard;
 import org.apache.openmeetings.db.dto.room.Whiteboard.ZoomMode;
@@ -70,6 +69,7 @@ import org.apache.openmeetings.db.entity.room.Room.RoomElement;
 import org.apache.openmeetings.db.entity.room.RoomFile;
 import org.apache.openmeetings.util.NullStringer;
 import org.apache.openmeetings.util.OmFileHelper;
+import org.apache.openmeetings.web.app.WhiteboardManager;
 import org.apache.openmeetings.web.common.NameDialog;
 import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.pdfbox.pdmodel.PDDocument;
@@ -155,8 +155,9 @@ public class WbPanel extends AbstractWbPanel {
 	@Override
 	void internalWbLoad(StringBuilder sb) {
 		Long langId = rp.getClient().getUser().getLanguageId();
-		if (!WhiteboardCache.contains(roomId) && rp.getRoom().getFiles() != null && !rp.getRoom().getFiles().isEmpty()) {
-			if (WhiteboardCache.tryLock(roomId)) {
+		WhiteboardManager wbm = getBean(WhiteboardManager.class);
+		if (!wbm.contains(roomId) && rp.getRoom().getFiles() != null && !rp.getRoom().getFiles().isEmpty()) {
+			if (wbm.tryLock(roomId)) {
 				try {
 					TreeMap<Long, List<BaseFileItem>> files = new TreeMap<>();
 					for (RoomFile rf : rp.getRoom().getFiles()) {
@@ -167,21 +168,21 @@ public class WbPanel extends AbstractWbPanel {
 						}
 						bfl.add(rf.getFile());
 					}
-					Whiteboards _wbs = WhiteboardCache.get(roomId, langId);
+					Whiteboards _wbs = wbm.get(roomId, langId);
 					for (Map.Entry<Long, List<BaseFileItem>> e : files.entrySet()) {
-						Whiteboard wb = WhiteboardCache.add(roomId, langId);
+						Whiteboard wb = wbm.add(roomId, langId);
 						_wbs.setActiveWb(wb.getId());
 						for (BaseFileItem fi : e.getValue()) {
 							sendFileToWb(fi, false);
 						}
 					}
 				} finally {
-					WhiteboardCache.unlock(roomId);
+					wbm.unlock(roomId);
 				}
 			}
 		}
-		Whiteboards wbs = WhiteboardCache.get(roomId, langId);
-		loadWhiteboards(sb, rp.getClient(), wbs, WhiteboardCache.list(roomId));
+		Whiteboards wbs = wbm.get(roomId, langId);
+		loadWhiteboards(sb, rp.getClient(), wbs, wbm.list(roomId));
 		JSONObject wbj = getWbJson(wbs.getActiveWb());
 		sb.append("WbArea.activateWb(").append(wbj).append(");");
 		Whiteboard wb = wbs.get(wbs.getActiveWb());
@@ -199,6 +200,7 @@ public class WbPanel extends AbstractWbPanel {
 	@Override
 	protected void processWbAction(WbAction a, JSONObject obj, AjaxRequestTarget target) throws IOException {
 		Client c = rp.getClient();
+		WhiteboardManager wbm = getBean(WhiteboardManager.class);
 		switch (a) {
 			case createObj:
 			case modifyObj:
@@ -241,7 +243,7 @@ public class WbPanel extends AbstractWbPanel {
 			{
 				StringBuilder sb = new StringBuilder("WbArea.initVideos(");
 				JSONArray arr = new JSONArray();
-				for (Entry<Long, Whiteboard> entry : WhiteboardCache.list(roomId)) {
+				for (Entry<Long, Whiteboard> entry : wbm.list(roomId)) {
 					Whiteboard wb = entry.getValue();
 					for (JSONObject o : wb.list()) {
 						String ft = o.optString(ATTR_FILE_TYPE);
@@ -273,7 +275,7 @@ public class WbPanel extends AbstractWbPanel {
 			switch (a) {
 				case createWb:
 				{
-					Whiteboard wb = WhiteboardCache.add(roomId, c.getUser().getLanguageId());
+					Whiteboard wb = wbm.add(roomId, c.getUser().getLanguageId());
 					sendWbAll(WbAction.createWb, getAddWbJson(wb));
 				}
 					break;
@@ -281,7 +283,7 @@ public class WbPanel extends AbstractWbPanel {
 				{
 					long id = obj.optLong("wbId", -1);
 					if (id > -1) {
-						WhiteboardCache.remove(roomId, id);
+						wbm.remove(roomId, id);
 						sendWbAll(WbAction.removeWb, obj);
 					}
 				}
@@ -290,26 +292,26 @@ public class WbPanel extends AbstractWbPanel {
 				{
 					long _id = obj.optLong("wbId", -1);
 					if (_id > -1) {
-						WhiteboardCache.activate(roomId, _id);
+						wbm.activate(roomId, _id);
 						sendWbAll(WbAction.activateWb, obj);
 					}
 				}
 					break;
 				case renameWb:
 				{
-					Whiteboard wb = WhiteboardCache.get(roomId).get(obj.optLong("wbId", -1));
+					Whiteboard wb = wbm.get(roomId).get(obj.optLong("wbId", -1));
 					if (wb != null) {
-						WhiteboardCache.update(roomId, wb.setName(obj.getString("name")));
+						wbm.update(roomId, wb.setName(obj.getString("name")));
 						sendWbAll(WbAction.renameWb, obj);
 					}
 				}
 					break;
 				case setSlide:
 				{
-					Whiteboard wb = WhiteboardCache.get(roomId).get(obj.optLong("wbId", -1));
+					Whiteboard wb = wbm.get(roomId).get(obj.optLong("wbId", -1));
 					if (wb != null) {
 						wb.setSlide(obj.optInt(ATTR_SLIDE, 0));
-						WhiteboardCache.update(roomId, wb);
+						wbm.update(roomId, wb);
 						sendWbOthers(WbAction.setSlide, obj);
 					}
 				}
@@ -321,10 +323,10 @@ public class WbPanel extends AbstractWbPanel {
 					break;
 				case setSize:
 				{
-					Whiteboard wb = WhiteboardCache.get(roomId).get(obj.getLong("wbId"));
+					Whiteboard wb = wbm.get(roomId).get(obj.getLong("wbId"));
 					wb.setZoom(obj.getDouble("zoom"));
 					wb.setZoomMode(ZoomMode.valueOf(obj.getString("zoomMode")));
-					WhiteboardCache.update(roomId, wb);
+					wbm.update(roomId, wb);
 					sendWbOthers(WbAction.setSize, getAddWbJson(wb));
 				}
 					break;
@@ -337,17 +339,17 @@ public class WbPanel extends AbstractWbPanel {
 			switch (a) {
 				case createObj:
 				{
-					Whiteboard wb = WhiteboardCache.get(roomId).get(obj.getLong("wbId"));
+					Whiteboard wb = wbm.get(roomId).get(obj.getLong("wbId"));
 					JSONObject o = obj.getJSONObject("obj");
 					wb.put(o.getString("uid"), o);
-					WhiteboardCache.update(roomId, wb);
+					wbm.update(roomId, wb);
 					addUndo(wb.getId(), new UndoObject(UndoObject.Type.add, o));
 					sendWbOthers(WbAction.createObj, obj);
 				}
 					break;
 				case modifyObj:
 				{
-					Whiteboard wb = WhiteboardCache.get(roomId).get(obj.getLong("wbId"));
+					Whiteboard wb = wbm.get(roomId).get(obj.getLong("wbId"));
 					JSONArray arr = obj.getJSONArray("obj");
 					JSONArray undo = new JSONArray();
 					for (int i = 0; i < arr.length(); ++i) {
@@ -360,7 +362,7 @@ public class WbPanel extends AbstractWbPanel {
 						}
 					}
 					if (arr.length() != 0) {
-						WhiteboardCache.update(roomId, wb);
+						wbm.update(roomId, wb);
 						addUndo(wb.getId(), new UndoObject(UndoObject.Type.modify, undo));
 					}
 					sendWbOthers(WbAction.modifyObj, obj);
@@ -368,7 +370,7 @@ public class WbPanel extends AbstractWbPanel {
 					break;
 				case deleteObj:
 				{
-					Whiteboard wb = WhiteboardCache.get(roomId).get(obj.getLong("wbId"));
+					Whiteboard wb = wbm.get(roomId).get(obj.getLong("wbId"));
 					JSONArray arr = obj.getJSONArray("obj");
 					JSONArray undo = new JSONArray();
 					for (int i = 0; i < arr.length(); ++i) {
@@ -379,7 +381,7 @@ public class WbPanel extends AbstractWbPanel {
 						}
 					}
 					if (undo.length() != 0) {
-						WhiteboardCache.update(roomId, wb);
+						wbm.update(roomId, wb);
 						addUndo(wb.getId(), new UndoObject(UndoObject.Type.remove, undo));
 					}
 					sendWbAll(WbAction.deleteObj, obj);
@@ -387,10 +389,10 @@ public class WbPanel extends AbstractWbPanel {
 					break;
 				case clearSlide:
 				{
-					Whiteboard wb = WhiteboardCache.get(roomId).get(obj.getLong("wbId"));
+					Whiteboard wb = wbm.get(roomId).get(obj.getLong("wbId"));
 					JSONArray arr = wb.clearSlide(obj.getInt(ATTR_SLIDE));
 					if (arr.length() != 0) {
-						WhiteboardCache.update(roomId, wb);
+						wbm.update(roomId, wb);
 						addUndo(wb.getId(), new UndoObject(UndoObject.Type.remove, arr));
 					}
 					sendWbAll(WbAction.clearSlide, obj);
@@ -405,13 +407,13 @@ public class WbPanel extends AbstractWbPanel {
 					Long wbId = obj.getLong("wbId");
 					UndoObject uo = getUndo(wbId);
 					if (uo != null) {
-						Whiteboard wb = WhiteboardCache.get(roomId).get(wbId);
+						Whiteboard wb = wbm.get(roomId).get(wbId);
 						switch (uo.getType()) {
 							case add:
 							{
 								JSONObject o = new JSONObject(uo.getObject());
 								wb.remove(o.getString("uid"));
-								WhiteboardCache.update(roomId, wb);
+								wbm.update(roomId, wb);
 								sendWbAll(WbAction.deleteObj, obj.put("obj", new JSONArray().put(o)));
 							}
 								break;
@@ -422,7 +424,7 @@ public class WbPanel extends AbstractWbPanel {
 									JSONObject o = arr.getJSONObject(i);
 									wb.put(o.getString("uid"), o);
 								}
-								WhiteboardCache.update(roomId, wb);
+								wbm.update(roomId, wb);
 								sendWbAll(WbAction.createObj, obj.put("obj", new JSONArray(uo.getObject())));
 							}
 								break;
@@ -433,7 +435,7 @@ public class WbPanel extends AbstractWbPanel {
 									JSONObject o = arr.getJSONObject(i);
 									wb.put(o.getString("uid"), o);
 								}
-								WhiteboardCache.update(roomId, wb);
+								wbm.update(roomId, wb);
 								sendWbAll(WbAction.modifyObj, obj.put("obj", arr));
 							}
 								break;
@@ -443,13 +445,13 @@ public class WbPanel extends AbstractWbPanel {
 					break;
 				case videoStatus:
 				{
-					Whiteboard wb = WhiteboardCache.get(roomId).get(obj.getLong("wbId"));
+					Whiteboard wb = wbm.get(roomId).get(obj.getLong("wbId"));
 					String uid = obj.getString("uid");
 					JSONObject po = wb.get(uid);
 					if (po != null && "video".equals(po.getString(ATTR_TYPE))) {
 						JSONObject ns = obj.getJSONObject(PARAM_STATUS);
 						po.put(PARAM_STATUS, ns.put("updated", System.currentTimeMillis()));
-						WhiteboardCache.update(roomId, wb.put(uid, po));
+						wbm.update(roomId, wb.put(uid, po));
 						obj.put(ATTR_SLIDE, po.getInt(ATTR_SLIDE));
 						sendWbAll(WbAction.videoStatus, obj);
 					}
@@ -517,7 +519,8 @@ public class WbPanel extends AbstractWbPanel {
 	}
 
 	private void clearAll(Long roomId, long wbId) {
-		Whiteboard wb = WhiteboardCache.get(roomId).get(wbId);
+		WhiteboardManager wbm = getBean(WhiteboardManager.class);
+		Whiteboard wb = wbm.get(roomId).get(wbId);
 		if (wb == null) {
 			return;
 		}
@@ -525,7 +528,7 @@ public class WbPanel extends AbstractWbPanel {
 		if (arr.length() != 0) {
 			addUndo(wb.getId(), new UndoObject(UndoObject.Type.remove, arr));
 		}
-		wb = WhiteboardCache.clear(roomId, wbId);
+		wb = wbm.clear(roomId, wbId);
 		sendWbAll(WbAction.clearAll, new JSONObject().put("wbId", wbId));
 		sendWbAll(WbAction.setSize, getAddWbJson(wb));
 	}
@@ -542,7 +545,8 @@ public class WbPanel extends AbstractWbPanel {
 	@Override
 	public void sendFileToWb(final BaseFileItem fi, boolean clean) {
 		if (isVisible() && fi.getId() != null) {
-			Whiteboards wbs = WhiteboardCache.get(roomId);
+			WhiteboardManager wbm = getBean(WhiteboardManager.class);
+			Whiteboards wbs = wbm.get(roomId);
 			String wuid = UUID.randomUUID().toString();
 			Whiteboard wb = wbs.get(wbs.getActiveWb());
 			switch (fi.getType()) {
@@ -561,7 +565,7 @@ public class WbPanel extends AbstractWbPanel {
 									return addFileUrl(rp.getClient(), wbs.getUid(), o, _f -> updateWbSize(wb, _f));
 								});
 							if (updated[0]) {
-								WhiteboardCache.update(roomId, wb);
+								wbm.update(roomId, wb);
 							}
 							sendWbAll(WbAction.setSize, getAddWbJson(wb));
 							sendWbAll(WbAction.load, getObjWbJson(wb.getId(), arr));
@@ -600,7 +604,7 @@ public class WbPanel extends AbstractWbPanel {
 					}
 					wb.put(wuid, file);
 					updateWbSize(wb, fi);
-					WhiteboardCache.update(roomId, wb);
+					wbm.update(roomId, wb);
 					sendWbAll(WbAction.setSize, getAddWbJson(wb));
 					WbWebSocketHelper.sendWbFile(roomId, wb.getId(), ruid, file, fi);
 				}
@@ -648,7 +652,7 @@ public class WbPanel extends AbstractWbPanel {
 	}
 
 	public static String saveWb(Long roomId, Long wbId, String name) {
-		Whiteboard wb = WhiteboardCache.get(roomId).get(wbId);
+		Whiteboard wb = getBean(WhiteboardManager.class).get(roomId).get(wbId);
 		FileItem f = new FileItem();
 		f.setType(BaseFileItem.Type.WmlFile);
 		f.setRoomId(roomId);
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java
index 0607aac..a510f49 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java
@@ -23,7 +23,6 @@ import static org.apache.openmeetings.core.util.WebSocketHelper.ID_ROOM_PREFIX;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DASHBOARD_SHOW_CHAT;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
 import static org.apache.openmeetings.web.app.Application.getBean;
-import static org.apache.openmeetings.web.app.Application.getUserRooms;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import static org.apache.openmeetings.web.room.RoomPanel.isModerator;
 import static org.apache.openmeetings.web.util.CallbackFunctionHelper.getNamedFunction;
@@ -46,6 +45,7 @@ import org.apache.openmeetings.db.entity.basic.ChatMessage;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.common.MainPanel;
 import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
@@ -165,7 +165,7 @@ public class Chat extends Panel {
 			ChatDao dao = getBean(ChatDao.class);
 			StringBuilder sb = new StringBuilder(getReinit());
 			List<ChatMessage> list = new ArrayList<>(dao.getGlobal(0, 30));
-			for(Long roomId : getUserRooms(getUserId())) {
+			for(Long roomId : getBean(ClientManager.class).listRoomIds(getUserId())) {
 				Room r = getBean(RoomDao.class).get(roomId);
 				sb.append(addRoom(r));
 			}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatForm.java
index de5ffa6..257c1bb 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatForm.java
@@ -24,7 +24,6 @@ import static org.apache.openmeetings.core.util.WebSocketHelper.ID_USER_PREFIX;
 import static org.apache.openmeetings.db.util.FormatHelper.getDisplayName;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
 import static org.apache.openmeetings.web.app.Application.getBean;
-import static org.apache.openmeetings.web.app.Application.isUserInRoom;
 import static org.apache.openmeetings.web.app.WebSession.getDateFormat;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import static org.apache.openmeetings.web.room.RoomPanel.isModerator;
@@ -43,6 +42,7 @@ import org.apache.openmeetings.db.entity.basic.ChatMessage;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.common.MainPanel;
 import org.apache.wicket.Component;
 import org.apache.wicket.ajax.AjaxRequestTarget;
@@ -112,7 +112,7 @@ public class ChatForm extends Form<Void> {
 					if (!process(
 							() -> getChat().isShowDashboardChat()
 							, r -> {
-								if (isUserInRoom(r.getId(), getUserId())) {
+								if (getBean(ClientManager.class).isInRoom(r.getId(), getUserId())) {
 									m.setToRoom(r);
 								} else {
 									log.error("It seems like we are being hacked!!!!");
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/UserSearchPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/UserSearchPanel.java
index 941c7f3..f3d1655 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/UserSearchPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/profile/UserSearchPanel.java
@@ -21,7 +21,6 @@ package org.apache.openmeetings.web.user.profile;
 import static org.apache.openmeetings.db.util.TimezoneUtil.getTimeZone;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.ATTR_CLASS;
 import static org.apache.openmeetings.web.app.Application.getBean;
-import static org.apache.openmeetings.web.app.Application.isUserOnline;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import static org.apache.openmeetings.web.util.CallbackFunctionHelper.addOnClick;
 
@@ -33,6 +32,7 @@ import java.util.List;
 import org.apache.openmeetings.db.dao.user.UserContactDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.common.PagingNavigatorPanel;
 import org.apache.openmeetings.web.common.UserBasePanel;
 import org.apache.wicket.AttributeModifier;
@@ -116,7 +116,7 @@ public class UserSearchPanel extends UserBasePanel {
 				final UserContactDao contactsDao = getBean(UserContactDao.class);
 				User u = item.getModelObject();
 				final long userId = u.getId();
-				item.add(new WebMarkupContainer("status").add(AttributeModifier.append(ATTR_CLASS, isUserOnline(userId) ? "online" : "offline")));
+				item.add(new WebMarkupContainer("status").add(AttributeModifier.append(ATTR_CLASS, getBean(ClientManager.class).isOnline(userId) ? "online" : "offline")));
 				item.add(new Label("name", getName(u)));
 				item.add(new Label("tz", getTimeZone(u).getID()));
 				item.add(new Label("offer", u.getUserOffers()));
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/record/RecordingResourceReference.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/record/RecordingResourceReference.java
index 771712e..32c6e64 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/record/RecordingResourceReference.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/record/RecordingResourceReference.java
@@ -19,7 +19,6 @@
 package org.apache.openmeetings.web.user.record;
 
 import static org.apache.openmeetings.web.app.Application.getBean;
-import static org.apache.openmeetings.web.app.Application.getOnlineClient;
 import static org.apache.openmeetings.web.app.WebSession.getExternalType;
 import static org.apache.openmeetings.web.app.WebSession.getRecordingId;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
@@ -27,7 +26,6 @@ import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import java.util.Map.Entry;
 
 import org.apache.directory.api.util.Strings;
-import org.apache.openmeetings.core.data.whiteboard.WhiteboardCache;
 import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.dao.user.GroupUserDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
@@ -37,7 +35,9 @@ import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.file.BaseFileItem.Type;
 import org.apache.openmeetings.db.entity.record.Recording;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.app.WebSession;
+import org.apache.openmeetings.web.app.WhiteboardManager;
 import org.apache.openmeetings.web.util.FileItemResourceReference;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.IResource.Attributes;
@@ -88,9 +88,9 @@ public abstract class RecordingResourceReference extends FileItemResourceReferen
 		if (id.equals(getRecordingId())) {
 			return r;
 		}
-		Client c = getOnlineClient(uid);
+		Client c = getBean(ClientManager.class).get(uid);
 		if (c != null && c.getRoom() != null) {
-			Whiteboards wbs = WhiteboardCache.get(c.getRoom().getId());
+			Whiteboards wbs = getBean(WhiteboardManager.class).get(c.getRoom().getId());
 			if (wbs != null && !Strings.isEmpty(ruid) && ruid.equals(wbs.getUid())) {
 				for (Entry<Long, Whiteboard> e : wbs.getWhiteboards().entrySet()) {
 					if (e.getValue().contains(r.getHash())) {
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomListPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomListPanel.java
index fd3fd33..2c71e12 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomListPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomListPanel.java
@@ -19,12 +19,13 @@
 package org.apache.openmeetings.web.user.rooms;
 
 import static org.apache.openmeetings.util.OpenmeetingsVariables.ATTR_TITLE;
+import static org.apache.openmeetings.web.app.Application.getBean;
 import static org.apache.openmeetings.web.common.BasePanel.EVT_CLICK;
 
 import java.util.List;
 
 import org.apache.openmeetings.db.entity.room.Room;
-import org.apache.openmeetings.web.app.Application;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.pages.MainPage;
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.ajax.AjaxEventBehavior;
@@ -70,7 +71,7 @@ public class RoomListPanel extends Panel {
 				final WebMarkupContainer info = new WebMarkupContainer("info");
 				roomContainer.add(info.setOutputMarkupId(true)
 						.add(AttributeModifier.append(ATTR_TITLE, getString(String.format("room.type.%s.desc", r.getType().name())))));
-				final Label curUsers = new Label("curUsers", new Model<>(Application.getRoomClients(r.getId()).size()));
+				final Label curUsers = new Label("curUsers", new Model<>(getBean(ClientManager.class).listByRoom(r.getId()).size()));
 				roomContainer.add(curUsers.setOutputMarkupId(true));
 				roomContainer.add(new Label("totalUsers", r.getCapacity()));
 				item.add(new Button("btn").add(new Label("label", label)).add(new RoomEnterBehavior(r.getId()) {
@@ -86,7 +87,7 @@ public class RoomListPanel extends Panel {
 
 					@Override
 					public void onClick(AjaxRequestTarget target) {
-						target.add(curUsers.setDefaultModelObject(Application.getRoomClients(r.getId()).size()));
+						target.add(curUsers.setDefaultModelObject(getBean(ClientManager.class).listByRoom(r.getId()).size()));
 						onRefreshClick(target, r);
 					}
 
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomsPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomsPanel.java
index 671719b..97a6bb2 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomsPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/rooms/RoomsPanel.java
@@ -30,7 +30,7 @@ import org.apache.openmeetings.db.dao.user.UserDao;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.util.OmFileHelper;
-import org.apache.openmeetings.web.app.Application;
+import org.apache.openmeetings.web.app.ClientManager;
 import org.apache.openmeetings.web.common.UserPanel;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.html.WebMarkupContainer;
@@ -123,7 +123,7 @@ public class RoomsPanel extends UserPanel {
 	}
 
 	void updateRoomDetails(AjaxRequestTarget target) {
-		clients.setDefaultModelObject(Application.getRoomClients(roomId));
+		clients.setDefaultModelObject(getBean(ClientManager.class).listByRoom(roomId));
 		Room room = getBean(RoomDao.class).get(roomId);
 		roomIdLbl.setDefaultModelObject(room.getId());
 		roomNameLbl.setDefaultModelObject(room.getName());
diff --git a/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/RoomWebService.java b/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/RoomWebService.java
index dbbcbfc..fcea5e7 100644
--- a/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/RoomWebService.java
+++ b/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/RoomWebService.java
@@ -38,7 +38,6 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 
 import org.apache.cxf.feature.Features;
-import org.apache.openmeetings.IApplication;
 import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.dao.room.InvitationDao;
 import org.apache.openmeetings.db.dao.room.RoomDao;
@@ -53,6 +52,7 @@ import org.apache.openmeetings.db.entity.room.Invitation.MessageType;
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.RoomFile;
 import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.db.manager.IClientManager;
 import org.apache.openmeetings.db.util.ws.RoomMessage;
 import org.apache.openmeetings.service.room.InvitationManager;
 import org.apache.openmeetings.webservice.error.ServiceException;
@@ -313,7 +313,6 @@ public class RoomWebService extends BaseWebService {
 	public List<RoomCountDTO> counters(@WebParam(name="sid") @QueryParam("sid") String sid, @WebParam(name="id") @QueryParam("id") List<Long> ids) {
 		return performCall(sid, User.Right.Soap, sd -> {
 			List<RoomCountDTO> roomBeans = new ArrayList<>();
-			IApplication app = getApp();
 			List<Room> rooms = getRoomDao().get(ids);
 
 			for (Room room : rooms) {
@@ -321,7 +320,7 @@ public class RoomWebService extends BaseWebService {
 				rCountBean.setRoomId(room.getId());
 				rCountBean.setRoomName(room.getName());
 				rCountBean.setMaxUser(room.getCapacity());
-				rCountBean.setRoomCount(app.getOmRoomClients(room.getId()).size());
+				rCountBean.setRoomCount(getBean(IClientManager.class).listByRoom(room.getId()).size());
 
 				roomBeans.add(rCountBean);
 			}
diff --git a/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/UserWebService.java b/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/UserWebService.java
index 86b8764..1e58ae5 100644
--- a/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/UserWebService.java
+++ b/openmeetings-webservice/src/main/java/org/apache/openmeetings/webservice/UserWebService.java
@@ -59,6 +59,7 @@ import org.apache.openmeetings.db.entity.server.Sessiondata;
 import org.apache.openmeetings.db.entity.user.Address;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.entity.user.User.Right;
+import org.apache.openmeetings.db.manager.IClientManager;
 import org.apache.openmeetings.service.user.UserManager;
 import org.apache.openmeetings.util.OmException;
 import org.apache.openmeetings.webservice.error.ServiceException;
@@ -354,6 +355,6 @@ public class UserWebService extends BaseWebService {
 	@GET
 	@Path("/count/{roomid}")
 	public ServiceResult count(@WebParam(name="sid") @QueryParam("sid") String sid, @WebParam(name="roomid") @PathParam("roomid") Long roomId) {
-		return performCall(sid, User.Right.Soap, sd -> new ServiceResult(String.valueOf(getApp().getOmRoomClients(roomId).size()), Type.SUCCESS));
+		return performCall(sid, User.Right.Soap, sd -> new ServiceResult(String.valueOf(getBean(IClientManager.class).listByRoom(roomId).size()), Type.SUCCESS));
 	}
 }

-- 
To stop receiving notification emails like this one, please contact
solomax@apache.org.