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:06 UTC

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

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.