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 2017/02/16 04:15:28 UTC

svn commit: r1783168 [3/4] - in /openmeetings/application: branches/3.2.x/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ branches/3.2.x/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/red5/ branches/3.2.x/openm...

Modified: openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/red5/ScopeApplicationAdapter.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/red5/ScopeApplicationAdapter.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/red5/ScopeApplicationAdapter.java (original)
+++ openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/red5/ScopeApplicationAdapter.java Thu Feb 16 04:15:27 2017
@@ -40,6 +40,7 @@ import org.apache.openmeetings.core.data
 import org.apache.openmeetings.core.remote.RecordingService;
 import org.apache.openmeetings.core.remote.WhiteBoardService;
 import org.apache.openmeetings.core.remote.util.SessionVariablesUtil;
+import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.calendar.AppointmentDao;
 import org.apache.openmeetings.db.dao.label.LabelDao;
@@ -59,6 +60,7 @@ import org.apache.openmeetings.db.entity
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.Room.RoomElement;
 import org.apache.openmeetings.db.entity.server.Server;
+import org.apache.openmeetings.db.entity.server.Sessiondata;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.util.AuthLevelUtil;
 import org.apache.openmeetings.util.CalendarPatterns;
@@ -69,9 +71,6 @@ import org.apache.openmeetings.util.Vers
 import org.apache.openmeetings.util.message.RoomMessage;
 import org.apache.openmeetings.util.message.TextRoomMessage;
 import org.apache.wicket.Application;
-import org.apache.wicket.protocol.ws.WebSocketSettings;
-import org.apache.wicket.protocol.ws.api.IWebSocketConnection;
-import org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry;
 import org.apache.wicket.util.string.StringValue;
 import org.apache.wicket.util.string.Strings;
 import org.red5.logging.Red5LoggerFactory;
@@ -147,11 +146,11 @@ public class ScopeApplicationAdapter ext
 			for (String scopeName : scope.getScopeNames()) {
 				log.debug("scopeName :: " + scopeName);
 			}
-			
+
 			InitializationContainer.initComplete = true;
 			Version.logOMStarted();
 			recordingDao.resetProcessingStatus(); //we are starting so all processing recordings are now errors
-			sessionManager.clearCache(); // 'sticky' clients should be cleaned up from DB 
+			sessionManager.clearCache(); // 'sticky' clients should be cleaned up from DB
 		} catch (Exception err) {
 			log.error("[appStart]", err);
 		}
@@ -161,11 +160,11 @@ public class ScopeApplicationAdapter ext
 	@SuppressWarnings("unchecked")
 	private static Map<String, Object> getConnParams(Object[] params) {
 		if (params != null && params.length > 0) {
-			return (Map<String, Object>)params[0]; 
+			return (Map<String, Object>)params[0];
 		}
 		return new HashMap<>();
 	}
-	
+
 	@Override
 	public boolean roomConnect(IConnection conn, Object[] params) {
 		log.debug("roomConnect : ");
@@ -185,6 +184,9 @@ public class ScopeApplicationAdapter ext
 		String uid = (String)connParams.get("uid");
 		String securityCode = (String)connParams.get(SECURITY_CODE_PARAM);
 		String parentSid = (String)map.get("parentSid");
+		if (parentSid == null) {
+			parentSid = (String)connParams.get("parentSid");
+		}
 		if (!Strings.isEmpty(securityCode)) {
 			//FIXME TODO add better mechanism, this is for external applications like ffmpeg
 			Client parent = sessionManager.getClientByPublicSID(securityCode, null);
@@ -202,10 +204,8 @@ public class ScopeApplicationAdapter ext
 		}
 
 		Client rcm = new Client();
-		Client parentClient = null;
-		//TODO add similar code for other connections
 		if (map.containsKey("screenClient")) {
-			parentClient = sessionManager.getClientByPublicSID(parentSid, null);
+			Client parentClient = sessionManager.getClientByPublicSID(parentSid, null);
 			if (parentClient == null) {
 				log.warn("Bad parent for screen-sharing client, client is rejected");
 				return rejectClient();
@@ -220,12 +220,24 @@ public class ScopeApplicationAdapter ext
 		StringValue scn = StringValue.valueOf(conn.getScope().getName());
 		rcm.setScope(scn.toString());
 		long roomId = scn.toLong(Long.MIN_VALUE);
+		boolean notHibernate = !"hibernate".equals(scn.toString());
 		if (Long.MIN_VALUE != roomId) {
 			rcm.setRoomId(roomId);
-		} else if (!"hibernate".equals(scn.toString())) {
+		} else if (notHibernate) {
 			log.warn("Bad room specified, client is rejected");
 			return rejectClient();
 		}
+		if (connParams.containsKey("mobileClient")) {
+			Sessiondata sd = sessiondataDao.check(parentSid);
+			if (sd.getUserId() == null && notHibernate) {
+				log.warn("Attempt of unauthorized room enter, client is rejected");
+				return rejectClient();
+			}
+			rcm.setMobile(true);
+			rcm.setUserId(sd.getUserId());
+			rcm.setSecurityCode(sd.getSessionId());
+			rcm.setPublicSID(UUID.randomUUID().toString());
+		}
 		rcm.setUserport(conn.getRemotePort());
 		rcm.setUserip(conn.getRemoteAddress());
 		rcm.setSwfurl(swfURL);
@@ -240,7 +252,7 @@ public class ScopeApplicationAdapter ext
 			log.warn("Failed to create Client on room connect");
 			return false;
 		}
-		
+
 		SessionVariablesUtil.initClient(conn.getClient(), rcm.getPublicSID());
 		//TODO add similar code for other connections, merge with above block
 		if (map.containsKey("screenClient")) {
@@ -283,15 +295,15 @@ public class ScopeApplicationAdapter ext
 					client.setStartStreaming(false);
 					//Send message to all users
 					sendMessageToCurrentScope("stopScreenSharingMessage", client, false);
-					broadcastRoom(new TextRoomMessage(client.getRoomId(), client.getUserId(), RoomMessage.Type.sharingStoped, client.getStreamPublishName()));
-					
+					WebSocketHelper.sendRoom(new TextRoomMessage(client.getRoomId(), client.getUserId(), RoomMessage.Type.sharingStoped, client.getStreamPublishName()));
+
 					returnMap.put("result", "stopSharingOnly");
 				}
 				if (Boolean.parseBoolean("" + map.get("stopRecording")) && client.getIsRecording()) {
 					changed = true;
 					client.setStartRecording(false);
 					client.setIsRecording(false);
-					
+
 					returnMap.put("result", "stopRecordingOnly");
 
 					recordingService.stopRecordAndSave(current.getScope(), client, null);
@@ -300,14 +312,14 @@ public class ScopeApplicationAdapter ext
 					changed = true;
 					client.setScreenPublishStarted(false);
 					returnMap.put("result", "stopPublishingOnly");
-					
+
 					//Send message to all users
 					sendMessageToCurrentScope("stopPublishingMessage", client, false);
 				}
-				
+
 				if (changed) {
 					sessionManager.updateClientByStreamId(client.getStreamid(), client, false, null);
-					
+
 					if (!client.isStartStreaming() && !client.isStartRecording() && !client.isStreamPublishStarted()) {
 						returnMap.put("result", "stopAll");
 					}
@@ -346,10 +358,10 @@ public class ScopeApplicationAdapter ext
 	}
 
 	/**
-	 * 
+	 *
 	 * @param map
 	 * @return returns key,value Map with multiple return values or null in case of exception
-	 * 
+	 *
 	 */
 	public Map<String, Object> setConnectionAsSharingClient(Map<String, Object> map) {
 		try {
@@ -396,12 +408,12 @@ public class ScopeApplicationAdapter ext
 				if (startStreaming) {
 					if (!alreadyStreaming) {
 						returnMap.put("modus", "startStreaming");
-	
+
 						log.debug("start streamPublishStart Is Screen Sharing ");
-						
+
 						//Send message to all users
 						sendMessageToCurrentScope("newScreenSharing", client, false);
-						broadcastRoom(new TextRoomMessage(client.getRoomId(), client.getUserId(), RoomMessage.Type.sharingStarted, client.getStreamPublishName()));
+						WebSocketHelper.sendRoom(new TextRoomMessage(client.getRoomId(), client.getUserId(), RoomMessage.Type.sharingStarted, client.getStreamPublishName()));
 					} else {
 						log.warn("Streaming is already started for the client id=" + client.getId() + ". Second request is ignored.");
 					}
@@ -409,9 +421,9 @@ public class ScopeApplicationAdapter ext
 				if (startRecording) {
 					if (!alreadyRecording) {
 						returnMap.put("modus", "startRecording");
-	
+
 						String recordingName = "Recording " + CalendarPatterns.getDateWithTimeByMiliSeconds(new Date());
-	
+
 						recordingService.recordMeetingStream(current, client, recordingName, "", false);
 					} else {
 						log.warn("Recording is already started for the client id=" + client.getId() + ". Second request is ignored.");
@@ -464,7 +476,7 @@ public class ScopeApplicationAdapter ext
 
 	/**
 	 * this function is invoked directly after initial connecting
-	 * 
+	 *
 	 * @return publicSID of current client
 	 */
 	public String getPublicSID() {
@@ -477,7 +489,7 @@ public class ScopeApplicationAdapter ext
 
 	/**
 	 * this function is invoked after a reconnect
-	 * 
+	 *
 	 * @param newPublicSID
 	 */
 	public boolean overwritePublicSID(String newPublicSID) {
@@ -502,7 +514,7 @@ public class ScopeApplicationAdapter ext
 	/**
 	 * Logic must be before roomDisconnect cause otherwise you cannot throw a
 	 * message to each one
-	 * 
+	 *
 	 */
 	@Override
 	public void roomLeave(IClient client, IScope room) {
@@ -525,11 +537,11 @@ public class ScopeApplicationAdapter ext
 	/**
 	 * this means a user has left a room but only logically, he didn't leave the
 	 * app he just left the room
-	 * 
+	 *
 	 * FIXME: Is this really needed anymore if you re-connect to another scope?
-	 * 
+	 *
 	 * Exit Room by Application
-	 * 
+	 *
 	 */
 	public void logicalRoomLeave() {
 		log.debug("logicalRoomLeave ");
@@ -550,10 +562,10 @@ public class ScopeApplicationAdapter ext
 	/**
 	 * Removes the Client from the List, stops recording, adds the Room-Leave
 	 * event to running recordings, clear Polls and removes Client from any list
-	 * 
-	 * This function is kind of private/protected as the client won't be able 
+	 *
+	 * This function is kind of private/protected as the client won't be able
 	 * to call it with proper values.
-	 * 
+	 *
 	 * @param client
 	 * @param scope
 	 */
@@ -561,10 +573,10 @@ public class ScopeApplicationAdapter ext
 		try {
 			log.debug("currentClient " + client);
 			Long roomId = client.getRoomId();
-			
+
 			if (client.isScreenClient() && client.isStartStreaming()) {
 				//TODO check others/find better way
-				broadcastRoom(new TextRoomMessage(client.getRoomId(), client.getUserId(), RoomMessage.Type.sharingStoped, client.getStreamPublishName()));
+				WebSocketHelper.sendRoom(new TextRoomMessage(client.getRoomId(), client.getUserId(), RoomMessage.Type.sharingStoped, client.getStreamPublishName()));
 			}
 
 			// Log the User
@@ -613,7 +625,7 @@ public class ScopeApplicationAdapter ext
 							log.debug("For this StreamId: " + cons.getClient().getId() + " There is no Client in the List anymore");
 							continue;
 						}
-						
+
 						//Do not send back to sender, but actually all other clients should receive this message swagner 01.10.2009
 						if (!client.getStreamid().equals(rcl.getStreamid())) {
 							// add Notification if another user isrecording
@@ -622,7 +634,7 @@ public class ScopeApplicationAdapter ext
 								log.debug("*** roomLeave Any Client is Recording - stop that");
 								recordingService.stopRecordingShowForClient(cons, client);
 							}
-							
+
 							boolean isScreen = rcl.isScreenClient();
 							if (isScreen && client.getPublicSID().equals(rcl.getStreamPublishName())) {
 								//going to terminate screen sharing started by this client
@@ -632,7 +644,7 @@ public class ScopeApplicationAdapter ext
 								// screen sharing clients do not receive events
 								continue;
 							}
-							
+
 							// Send to all connected users
 							((IServiceCapableConnection) cons).invoke("roomDisconnect", new Object[] { client },this);
 							log.debug("sending roomDisconnect to " + cons);
@@ -641,6 +653,10 @@ public class ScopeApplicationAdapter ext
 				}
 			}
 
+			if (client.isMobile()) {
+				IApplication app = (IApplication)Application.get(OpenmeetingsVariables.wicketApplicationName);
+				app.exit(client.getPublicSID());
+			}
 			if (removeUserFromSessionList) {
 				sessionManager.removeClient(client.getStreamid(), null);
 			}
@@ -652,7 +668,7 @@ public class ScopeApplicationAdapter ext
 	/**
 	 * This method handles the Event after a stream has been added all connected
 	 * Clients in the same room will get a notification
-	 * 
+	 *
 	 */
 	/* (non-Javadoc)
 	 * @see org.red5.server.adapter.MultiThreadedApplicationAdapter#streamPublishStart(org.red5.server.api.stream.IBroadcastStream)
@@ -665,10 +681,10 @@ public class ScopeApplicationAdapter ext
 			final String streamid = current.getClient().getId();
 			final Client currentClient = sessionManager.getClientByStreamId(streamid, null);
 
-			//We make a second object the has the reference to the object 
+			//We make a second object the has the reference to the object
 			//that we will use to send to all participents
 			Client clientObjectSendToSync = currentClient;
-			
+
 			// Notify all the clients that the stream had been started
 			log.debug("start streamPublishStart broadcast start: " + stream.getPublishedName() + " CONN " + current);
 
@@ -684,7 +700,7 @@ public class ScopeApplicationAdapter ext
 				currentClient.setVHeight(240);
 				sessionManager.updateClientByStreamId(streamid, currentClient, false, null);
 			}
-			
+
 			log.debug("newStream SEND: " + currentClient);
 
 			// Notify all users of the same Scope
@@ -693,14 +709,14 @@ public class ScopeApplicationAdapter ext
 				@Override
 				public boolean filter(IConnection conn) {
 					Client rcl = sessionManager.getClientByStreamId(conn.getClient().getId(), null);
-					
+
 					if (rcl == null) {
 						log.debug("RCL IS NULL newStream SEND");
 						return true;
 					}
-					
+
 					log.debug("check send to "+rcl);
-					
+
 					if (rcl.getPublicSID() == "") {
 						log.debug("publicSID IS NULL newStream SEND");
 						return true;
@@ -713,7 +729,7 @@ public class ScopeApplicationAdapter ext
 						log.debug("RCL getIsScreenClient newStream SEND");
 						return true;
 					}
-					
+
 					if (rcl.getPublicSID().equals(currentClient.getPublicSID())) {
 						log.debug("RCL publicSID is equal newStream SEND");
 						return true;
@@ -739,7 +755,7 @@ public class ScopeApplicationAdapter ext
 	/**
 	 * This method handles the Event after a stream has been removed all
 	 * connected Clients in the same room will get a notification
-	 * 
+	 *
 	 */
 	/* (non-Javadoc)
 	 * @see org.red5.server.adapter.MultiThreadedApplicationAdapter#streamBroadcastClose(org.red5.server.api.stream.IBroadcastStream)
@@ -760,9 +776,9 @@ public class ScopeApplicationAdapter ext
 
 	/**
 	 * This method handles the notification room-based
-	 * 
+	 *
 	 * @return void
-	 * 
+	 *
 	 */
 	private void sendClientBroadcastNotifications(IBroadcastStream stream, String clientFunction, Client rc) {
 		try {
@@ -917,7 +933,7 @@ public class ScopeApplicationAdapter ext
 	 * cause if the room has no moderator yet there is no-one he can ask to get
 	 * the moderation, in case its a Non-Moderated Room he should then get the
 	 * Moderation without any confirmation needed
-	 * 
+	 *
 	 * @return Long 1 => means get Moderation, 2 => ask Moderator for
 	 *         Moderation, 3 => wait for Moderator
 	 */
@@ -951,9 +967,9 @@ public class ScopeApplicationAdapter ext
 
 	/**
 	 * This method is used to set/update broadCastID of current client
-	 * 
-	 * @param updateBroadcastId boolean flag 
-	 * 
+	 *
+	 * @param updateBroadcastId boolean flag
+	 *
 	 * @return BroadcastId in case of no errors, -1 otherwise
 	 */
 	public long setUserAVSettings(boolean updateBroadcastId) {
@@ -1011,7 +1027,7 @@ public class ScopeApplicationAdapter ext
 
 	/**
 	 * This function is called once a User enters a Room
-	 * 
+	 *
 	 * It contains several different mechanism depending on what roomtype and
 	 * what options are available for the room to find out if the current user
 	 * will be a moderator of that room or not<br/>
@@ -1069,7 +1085,7 @@ public class ScopeApplicationAdapter ext
 			conferenceLogDao.add(ConferenceLog.Type.roomEnter,
 					client.getUserId(), streamid, roomId,
 					client.getUserip(), "");
-			
+
 			// Check for Moderation LogicalRoom ENTER
 			List<Client> roomClients = sessionManager.getClientListByRoom(roomId);
 
@@ -1090,11 +1106,11 @@ public class ScopeApplicationAdapter ext
 				sessionManager.updateClientByStreamId(streamid, client, false, null);
 
 				List<Client> modRoomList = sessionManager.getCurrentModeratorByRoom(client.getRoomId());
-				
+
 				//Sync message to everybody
 				sendMessageToCurrentScope("setNewModeratorByList", modRoomList, false);
 			}
-			
+
 			//Sync message to everybody
 			sendMessageToCurrentScope("addNewUser", client, false);
 
@@ -1119,57 +1135,8 @@ public class ScopeApplicationAdapter ext
 	}
 
 	/**
-	 * This method is invoked when the user has disconnected and reconnects to
-	 * the Gateway with the new scope
-	 * 
-	 * @param SID
-	 * @param userId
-	 * @param username
-	 * @param firstname
-	 * @param lastname
-	 * @param picture_uri
-	 * @return client being updated in case of success, null otherwise
-	 */
-	public Client setUsernameReconnect(String SID, Long userId, String username, String firstname, String lastname, String picture_uri) {
-		try {
-			log.debug("-----------  setUsernameReconnect");
-			IConnection current = Red5.getConnectionLocal();
-			String streamid = current.getClient().getId();
-			Client currentClient = sessionManager.getClientByStreamId(streamid, null);
-
-			SessionVariablesUtil.setUserId(current.getClient(), userId);
-			currentClient.setPicture_uri(picture_uri);
-			currentClient.setUserObject(userId, username, firstname, lastname);
-
-			// Update Session Data
-			sessiondataDao.updateUserWithoutSession(SID, userId);
-
-			// only fill this value from User-Record
-			// cause invited users have no associated User, so
-			// you cannot set the firstname,lastname from the UserRecord
-			if (userId != null) {
-				User us = userDao.get(userId);
-				
-				if (us != null) {
-					currentClient.setExternalUserId(us.getExternalId());
-					currentClient.setExternalUserType(us.getExternalType());
-					if (us.getPictureuri() != null) {
-						// set Picture-URI
-						currentClient.setPicture_uri(us.getPictureuri());
-					}
-				}
-			}
-			sessionManager.updateClientByStreamId(streamid, currentClient, false, null);
-			return currentClient;
-		} catch (Exception err) {
-			log.error("[setUsername]", err);
-		}
-		return null;
-	}
-
-	/**
 	 * this is set initial directly after login/loading language
-	 * 
+	 *
 	 * @param SID - id of the session
 	 * @param userId - id of the user being set
 	 * @param username - username of the user
@@ -1218,7 +1185,7 @@ public class ScopeApplicationAdapter ext
 
 	/**
 	 * used by the Screen-Sharing Servlet to trigger events
-	 * 
+	 *
 	 * @param roomId
 	 * @param message
 	 * @return the list of room clients
@@ -1261,7 +1228,7 @@ public class ScopeApplicationAdapter ext
 
 	/**
 	 * This Function is triggered from the Whiteboard
-	 * 
+	 *
 	 * @param whiteboardObjParam - array of parameters being sended to whiteboard
 	 * @param whiteboardId - id of whiteboard parameters will be send to
 	 * @return 1 in case of no errors, -1 otherwise
@@ -1276,7 +1243,7 @@ public class ScopeApplicationAdapter ext
 			return -1;
 		}
 	}
-	
+
 	private static Point getSize(FileItem fi) {
 		Point result = new Point(0, 0);
 		if (fi.getFlvWidth() != null && fi.getFlvHeight() != null) {
@@ -1285,7 +1252,7 @@ public class ScopeApplicationAdapter ext
 		}
 		return result;
 	}
-	
+
 	private static List<?> getWbObject(FileItem fi, String url) {
 		String fuid = UUID.randomUUID().toString();
 		Point size = getSize(fi);
@@ -1330,7 +1297,7 @@ public class ScopeApplicationAdapter ext
 				, fuid // this.currentlayer.name //-1
 				);
 	}
-	
+
 	private static List<?> getFlvWbObject(FileItem fi) {
 		String fuid = UUID.randomUUID().toString();
 		Point size = getSize(fi);
@@ -1341,17 +1308,17 @@ public class ScopeApplicationAdapter ext
 				, false // 3: false //playRemote
 				, size.x // 4: 416
 				, size.y // 5: 240
-				, 0 // 6: 1 // z-index 
-				, fi.getHash() // 7: null //TODO 
+				, 0 // 6: 1 // z-index
+				, fi.getHash() // 7: null //TODO
 				, 0 // 8: 0 //TODO // counter
 				, 0 // 9: 0 //TODO // x
 				, 0 // 10: 0 //TODO // y
 				, size.x // 11: 749 // width
 				, size.y // 12: 739 // height
-				, fuid // 13: 'flv_1469602000351' 
+				, fuid // 13: 'flv_1469602000351'
 				);
 	}
-	
+
 	public void sendToWhiteboard(String uid, Long wbId, FileItem fi, String url, boolean clean) {
 		ClientSessionInfo csi = sessionManager.getClientByPublicSIDAnyServer(uid);
 		if (csi == null) {
@@ -1359,7 +1326,7 @@ public class ScopeApplicationAdapter ext
 			return;
 		}
 		Client client = csi.getRcl();
-		
+
 		List<?> wbObject = new ArrayList<>();
 		switch (fi.getType()) {
 			case Image:
@@ -1382,14 +1349,14 @@ public class ScopeApplicationAdapter ext
 		}
 		sendToWhiteboard(client, Arrays.asList("whiteboard", new Date(), "draw", wbObject), wbId);
 	}
-	
+
 	private int sendToWhiteboard(Client client, List<?> wbObj, Long wbId) {
 		try {
 			// Check if this User is the Mod:
 			if (client == null) {
 				return -1;
 			}
-			
+
 			Map<Integer, Object> whiteboardObj = new HashMap<>();
 			int i = 0;
 			for (Object obj : wbObj) {
@@ -1451,7 +1418,7 @@ public class ScopeApplicationAdapter ext
 		sendMessageToCurrentScope("sendVarsToMessage", newMessage, false);
 		return 1;
 	}
-	
+
 	public int sendMessageAll(Object newMessage) {
 		sendMessageToCurrentScope("sendVarsToMessage", newMessage, true);
 		return 1;
@@ -1485,7 +1452,7 @@ public class ScopeApplicationAdapter ext
 			}
 
 			current.getScope().setAttribute("browserStatus", browserStatus);
-			
+
 			sendMessageToCurrentScope("sendVarsToMessage", newMessage, false);
 
 		} catch (Exception err) {
@@ -1502,9 +1469,9 @@ public class ScopeApplicationAdapter ext
 		//Sync to all users of current scope
 		sendMessageToCurrentScope("sendVarsToMessage", newMessage, false);
 	}
-	
+
 	/**
-	 * General sync mechanism for all messages that are send from within the 
+	 * General sync mechanism for all messages that are send from within the
 	 * scope of the current client, but:
 	 * <ul>
 	 * <li>optionally do not send to self (see param: sendSelf)</li>
@@ -1512,7 +1479,7 @@ public class ScopeApplicationAdapter ext
 	 * <li>do not send to clients that are audio/video clients (or potentially ones)</li>
 	 * <li>do not send to connections where no RoomClient is registered</li>
 	 * </ul>
-	 *  
+	 *
 	 * @param remoteMethodName The method to be called
 	 * @param newMessage parameters
 	 * @param sendSelf send to the current client as well
@@ -1520,7 +1487,7 @@ public class ScopeApplicationAdapter ext
 	public void sendMessageToCurrentScope(String remoteMethodName, Object newMessage, boolean sendSelf) {
 		sendMessageToCurrentScope(remoteMethodName, newMessage, sendSelf, false);
 	}
-	
+
 	public void sendToScope(final Long roomId, String method, Object obj) {
 		new MessageSender(getRoomScope("" + roomId), method, obj) {
 			@Override
@@ -1531,10 +1498,10 @@ public class ScopeApplicationAdapter ext
 			}
 		}.start();
 	}
-	
+
 	/**
 	 * Only temporary for load test, with return argument for the client to have a result
-	 * 
+	 *
 	 * @param remoteMethodName
 	 * @param newMessage
 	 * @param sendSelf
@@ -1545,10 +1512,9 @@ public class ScopeApplicationAdapter ext
 		sendMessageToCurrentScope(remoteMethodName, newMessage, sendSelf, false);
 		return true;
 	}
-	
-	
+
 	/**
-	 * General sync mechanism for all messages that are send from within the 
+	 * General sync mechanism for all messages that are send from within the
 	 * scope of the current client, but:
 	 * <ul>
 	 * <li>optionally do not send to self (see param: sendSelf)</li>
@@ -1556,7 +1522,7 @@ public class ScopeApplicationAdapter ext
 	 * <li>do not send to clients that are audio/video clients (or potentially ones)</li>
 	 * <li>do not send to connections where no RoomClient is registered</li>
 	 * </ul>
-	 *  
+	 *
 	 * @param remoteMethodName The method to be called
 	 * @param newMessage parameters
 	 * @param sendSelf send to the current client as well
@@ -1578,28 +1544,28 @@ public class ScopeApplicationAdapter ext
 		final IConnection current;
 		final String remoteMethodName;
 		final Object newMessage;
-		
+
 		public MessageSender(final String remoteMethodName, final Object newMessage) {
 			this((IScope)null, remoteMethodName, newMessage);
 		}
-		
+
 		public MessageSender(IScope _scope, String remoteMethodName, Object newMessage) {
 			this(Red5.getConnectionLocal(), _scope, remoteMethodName, newMessage);
 		}
-		
+
 		public MessageSender(IConnection current, String remoteMethodName, Object newMessage) {
 			this(current, null, remoteMethodName, newMessage);
 		}
-		
+
 		public MessageSender(IConnection current, IScope _scope, String remoteMethodName, Object newMessage) {
 			this.current = current;
 			scope = _scope == null ? current.getScope() : _scope;
 			this.remoteMethodName = remoteMethodName;
 			this.newMessage = newMessage;
 		}
-		
+
 		public abstract boolean filter(IConnection conn);
-		
+
 		@Override
 		public void run() {
 			try {
@@ -1645,7 +1611,7 @@ public class ScopeApplicationAdapter ext
 		}
 		return 1;
 	}
-	
+
 	/**
 	 * wrapper method
 	 * @param newMessage
@@ -1660,7 +1626,7 @@ public class ScopeApplicationAdapter ext
 			HashMap<String, Object> hsm = new HashMap<String, Object>();
 			hsm.put("client", currentClient);
 			hsm.put("message", newMessage);
-			
+
 			//Sync to all users of current scope
 			sendMessageToCurrentScope("sendVarsToMessageWithClient", hsm, sync);
 
@@ -1672,9 +1638,9 @@ public class ScopeApplicationAdapter ext
 	}
 
 	/**
-	 * Function is used to send the kick Trigger at the moment, 
+	 * Function is used to send the kick Trigger at the moment,
 	 * it sends a general message to a specific clientId
-	 * 
+	 *
 	 * @param newMessage
 	 * @param clientId
 	 * @return 1 in case of success, -1 otherwise
@@ -1705,7 +1671,7 @@ public class ScopeApplicationAdapter ext
 
 	/**
 	 * Sends a message to a user in the same room by its clientId
-	 * 
+	 *
 	 * @param newMessage
 	 * @param clientId
 	 * @return 1 in case of no exceptions, -1 otherwise
@@ -1731,7 +1697,7 @@ public class ScopeApplicationAdapter ext
 		}
 		return 1;
 	}
-	
+
 	public void sendMessageWithClientByPublicSID(Object message, String publicSID) {
 		try {
 			if (publicSID == null) {
@@ -1758,7 +1724,7 @@ public class ScopeApplicationAdapter ext
 						// screen sharing clients do not receive events
 						continue;
 					}
-					
+
 					if (publicSID.equals(SessionVariablesUtil.getPublicSID(client))) {
 						((IServiceCapableConnection) conn).invoke("newMessageByRoomAndDomain", new Object[] { message }, this);
 					}
@@ -1777,6 +1743,7 @@ public class ScopeApplicationAdapter ext
 	 *             recording instead of iterating through connections!
 	 * @return true in case there is recording session, false otherwise, null if any exception happend
 	 */
+	@Deprecated
 	public boolean getInterviewRecordingStatus() {
 		try {
 			IConnection current = Red5.getConnectionLocal();
@@ -1800,6 +1767,7 @@ public class ScopeApplicationAdapter ext
 	 * @deprecated @see {@link ScopeApplicationAdapter#getInterviewRecordingStatus()}
 	 * @return - false if there were existing recording, true if recording was started successfully, null if any exception happens
 	 */
+	@Deprecated
 	public boolean startInterviewRecording() {
 		try {
 			log.debug("-----------  startInterviewRecording");
@@ -1850,7 +1818,7 @@ public class ScopeApplicationAdapter ext
 	@SuppressWarnings({ "rawtypes" })
 	public boolean sendRemoteCursorEvent(final String streamid, Map messageObj) {
 		new MessageSender("sendRemoteCursorEvent", messageObj) {
-			
+
 			@Override
 			public boolean filter(IConnection conn) {
 				IClient client = conn.getClient();
@@ -1876,10 +1844,10 @@ public class ScopeApplicationAdapter ext
 		}
 		return recordingId;
 	}
-	
+
 	/**
 	 * Stop the recording of the streams and send event to connected users of scope
-	 * 
+	 *
 	 * @return true if interview was found
 	 */
 	public boolean stopInterviewRecording() {
@@ -1887,10 +1855,10 @@ public class ScopeApplicationAdapter ext
 		Client currentClient = sessionManager.getClientByStreamId(current.getClient().getId(), null);
 		return _stopInterviewRecording(currentClient, current.getScope());
 	}
-	
+
 	/**
 	 * Stop the recording of the streams and send event to connected users of scope
-	 * 
+	 *
 	 * @return true if interview was found
 	 */
 	private boolean _stopInterviewRecording(Client currentClient, IScope currentScope) {
@@ -1926,7 +1894,7 @@ public class ScopeApplicationAdapter ext
 	/**
 	 * Get all ClientList Objects of that room and domain Used in
 	 * lz.applyForModeration.lzx
-	 * 
+	 *
 	 * @return all ClientList Objects of that room
 	 */
 	public List<Client> getClientListScope() {
@@ -1944,7 +1912,7 @@ public class ScopeApplicationAdapter ext
 	private boolean getWhiteboardDrawStatus() {
 		return configurationDao.getWhiteboardDrawStatus();
 	}
-	
+
 	public String getCryptKey() {
 		return configurationDao.getCryptKey();
 	}
@@ -1976,7 +1944,7 @@ public class ScopeApplicationAdapter ext
 		}
 		return result.isEmpty() ? result : roomDao.getSipRooms(result);
 	}
-	
+
 	public List<Long> getActiveRoomIds() {
 		List<Long> result = getVerifiedActiveRoomIds(null);
 		for (Server s : serverDao.getActiveServers()) {
@@ -1984,15 +1952,15 @@ public class ScopeApplicationAdapter ext
 		}
 		return result.isEmpty() ? result : roomDao.getSipRooms(result);
 	}
-	
+
 	private String getSipTransportLastname(Long roomId) {
 		return getSipTransportLastname(roomManager.getSipConferenceMembersNumber(roomId));
 	}
-	
+
 	private static String getSipTransportLastname(Integer c) {
 		return (c != null && c > 0) ? "(" + (c - 1) + ")" : "";
 	}
-	
+
 	public synchronized int updateSipTransport() {
 		log.debug("-----------  updateSipTransport");
 		IConnection current = Red5.getConnectionLocal();
@@ -2035,16 +2003,4 @@ public class ScopeApplicationAdapter ext
 
 		sendMessageToCurrentScope("addNewUser", currentClient, false);
 	}
-	
-	public void broadcastRoom(RoomMessage msg) {
-		log.debug("Sending WebSocket message: {} {}", msg.getType(), msg instanceof TextRoomMessage ? ((TextRoomMessage)msg).getText() : "");
-		Application app = Application.get(OpenmeetingsVariables.wicketApplicationName);
-		WebSocketSettings settings = WebSocketSettings.Holder.get(app);
-		IWebSocketConnectionRegistry registry = settings.getConnectionRegistry();
-		for (IWebSocketConnection wc : registry.getConnections(app)) {
-			if (wc != null && wc.isOpen()) {
-				wc.sendMessage(msg);
-			}
-		}
-	}
 }

Modified: openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/session/SessionManager.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/session/SessionManager.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/session/SessionManager.java (original)
+++ openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/session/SessionManager.java Thu Feb 16 04:15:27 2017
@@ -42,25 +42,25 @@ import org.springframework.beans.factory
 
 /**
  * Handle {@link Client} objects.
- * 
+ *
  * Use a kind of decorator pattern to inject the {@link Server} into every call.
- * 
+ *
  * @author sebawagner
- * 
+ *
  */
 public class SessionManager implements ISessionManager {
 	protected static final Logger log = Red5LoggerFactory.getLogger(SessionManager.class, webAppRootKey);
-	
+
 	@Autowired
 	private ServerUtil serverUtil;
-	
+
 	/**
 	 * Injected via Spring, needs a getter/setter because it can be configured
 	 * Autowired will not suit here as there are multiple implementations of the
 	 * {@link IClientPersistenceStore}
 	 */
 	private IClientPersistenceStore cache;
-	
+
 	public IClientPersistenceStore getCache() {
 		return cache;
 	}
@@ -73,9 +73,12 @@ public class SessionManager implements I
 	public void clearCache() {
 		cache.clear();
 	}
-	
+
 	@Override
 	public Client add(Client c, Server server) {
+		if (c == null) {
+			return null;
+		}
 		if (server == null) {
 			server = serverUtil.getCurrentServer();
 		}
@@ -94,7 +97,7 @@ public class SessionManager implements I
 		cache.put(c.getStreamid(), c);
 		return c;
 	}
-	
+
 	@Override
 	public Client addClientListItem(String streamId, String scopeName,
 			int remotePort, String remoteAddress, String swfUrl, Server server) {
@@ -134,7 +137,7 @@ public class SessionManager implements I
 	public Collection<Client> getClients() {
 		return cache.getClients();
 	}
-	
+
 	@Override
 	public Collection<Client> getClientsWithServer() {
 		return cache.getClientsWithServer();
@@ -192,7 +195,7 @@ public class SessionManager implements I
 				if (rcl.isScreenClient()) {
 					continue;
 				}
-				
+
 				return rcl;
 			}
 		} catch (Exception err) {
@@ -239,7 +242,7 @@ public class SessionManager implements I
 		}
 		try {
 			Client rclSaved = cache.get(server, streamId);
-			
+
 			if (rclSaved != null) {
 				cache.put(streamId, rcm);
 				return true;

Added: openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java?rev=1783168&view=auto
==============================================================================
--- openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java (added)
+++ openmeetings/application/trunk/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java Thu Feb 16 04:15:27 2017
@@ -0,0 +1,128 @@
+/*
+ * 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.util;
+
+import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import org.apache.openmeetings.IApplication;
+import org.apache.openmeetings.db.entity.basic.Client;
+import org.apache.openmeetings.util.OpenmeetingsVariables;
+import org.apache.openmeetings.util.message.RoomMessage;
+import org.apache.openmeetings.util.message.TextRoomMessage;
+import org.apache.wicket.Application;
+import org.apache.wicket.protocol.ws.WebSocketSettings;
+import org.apache.wicket.protocol.ws.api.IWebSocketConnection;
+import org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry;
+import org.apache.wicket.protocol.ws.api.registry.PageIdKey;
+import org.apache.wicket.protocol.ws.concurrent.Executor;
+import org.red5.logging.Red5LoggerFactory;
+import org.slf4j.Logger;
+
+public class WebSocketHelper {
+	private static final Logger log = Red5LoggerFactory.getLogger(WebSocketHelper.class, webAppRootKey);
+
+	public static void sendClient(final Client c, byte[] b) {
+		if (c != null) {
+			send(a -> Arrays.asList(c), (t) -> {
+				try {
+					t.sendMessage(b, 0, b.length);
+				} catch (IOException e) {
+					log.error("Error while broadcasting byte[] to room", e);
+				}
+			}, null);
+		}
+	}
+
+	public static void sendRoom(final RoomMessage m) {
+		log.debug("Sending WebSocket message: {} {}", m.getType(), m instanceof TextRoomMessage ? ((TextRoomMessage)m).getText() : "");
+		sendRoom(m.getRoomId(), (t) -> t.sendMessage(m), null);
+	}
+
+	public static void sendRoom(final Long roomId, final String m) {
+		sendRoom(roomId, m, null);
+	}
+
+	public static void sendRoom(final Long roomId, final String m, Predicate<Client> check) {
+		log.debug("Sending WebSocket message: {}", m);
+		sendRoom(roomId, (t) -> {
+			try {
+				t.sendMessage(m);
+			} catch (IOException e) {
+				log.error("Error while broadcasting message to room", e);
+			}
+		}, check);
+	}
+
+	public static void sendUser(final Long userId, final String m) {
+		send(a -> ((IApplication)a).getOmClients(userId), (t) -> {
+			try {
+				t.sendMessage(m);
+			} catch (IOException e) {
+				log.error("Error while sending message to user", e);
+			}
+		}, null);
+	}
+
+	//TODO should this be unified???
+	public static void sendAll(final String m) {
+		Application app = Application.get(OpenmeetingsVariables.wicketApplicationName);
+		WebSocketSettings settings = WebSocketSettings.Holder.get(app);
+		IWebSocketConnectionRegistry reg = settings.getConnectionRegistry();
+		Executor executor = settings.getWebSocketPushMessageExecutor();
+		for (IWebSocketConnection c : reg.getConnections(app)) {
+			executor.run(() -> {
+				try {
+					c.sendMessage(m);
+				} catch (IOException e) {
+					log.error("Error while sending message to ALL", e);
+				}
+			});
+		}
+	}
+
+	public static void sendRoom(final Long roomId, Consumer<IWebSocketConnection> consumer, Predicate<Client> check) {
+		send(a -> ((IApplication)a).getOmRoomClients(roomId), consumer, check);
+	}
+
+	public static void send(
+			final Function<Application, List<Client>> func
+			, Consumer<IWebSocketConnection> consumer
+			, Predicate<Client> check)
+	{
+		Application app = Application.get(OpenmeetingsVariables.wicketApplicationName);
+		WebSocketSettings settings = WebSocketSettings.Holder.get(app);
+		IWebSocketConnectionRegistry reg = settings.getConnectionRegistry();
+		Executor executor = settings.getWebSocketPushMessageExecutor();
+		for (Client c : func.apply(app)) {
+			if (check == null || (check != null && check.test(c))) {
+				final IWebSocketConnection wc = reg.getConnection(app, c.getSessionId(), new PageIdKey(c.getPageId()));
+				if (wc != null && wc.isOpen()) {
+					executor.run(() -> consumer.accept(wc));
+				}
+			}
+		}
+	}
+}

Modified: openmeetings/application/trunk/openmeetings-db/src/main/java/org/apache/openmeetings/IApplication.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-db/src/main/java/org/apache/openmeetings/IApplication.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-db/src/main/java/org/apache/openmeetings/IApplication.java (original)
+++ openmeetings/application/trunk/openmeetings-db/src/main/java/org/apache/openmeetings/IApplication.java Thu Feb 16 04:15:27 2017
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings;
 
+import java.util.List;
 import java.util.Locale;
 import java.util.function.Supplier;
 
@@ -39,8 +40,11 @@ public interface IApplication {
 	String getOmString(String key, long languageId);
 	String getOmString(String key, final Locale loc, String... params);
 	Client updateClient(Client rcl);
+	List<org.apache.openmeetings.db.entity.basic.Client> getOmRoomClients(Long roomId);
+	List<org.apache.openmeetings.db.entity.basic.Client> getOmClients(Long userId);
 	String getOmContactsLink();
 	String getOmInvitationLink(Invitation i);
 	String urlForActivatePage(PageParameters pp);
 	void invalidateClient(Long userId, String sessionId);
+	void exit(String uid);
 }

Copied: openmeetings/application/trunk/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java (from r1783167, openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Client.java)
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java?p2=openmeetings/application/trunk/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java&p1=openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Client.java&r1=1783167&r2=1783168&rev=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Client.java (original)
+++ openmeetings/application/trunk/openmeetings-db/src/main/java/org/apache/openmeetings/db/entity/basic/Client.java Thu Feb 16 04:15:27 2017
@@ -16,9 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.openmeetings.web.app;
-
-import static org.apache.openmeetings.web.app.Application.getBean;
+package org.apache.openmeetings.db.entity.basic;
 
 import java.util.Date;
 import java.util.HashSet;
@@ -66,18 +64,27 @@ public class Client implements IDataProv
 	private int width = 0;
 	private int height = 0;
 
-	public Client(String sessionId, Long userId) {
-		this(sessionId, 0, userId);
+	public Client(String sessionId, Long userId, UserDao dao) {
+		this(sessionId, 0, userId, dao);
 	}
 
-	public Client(String sessionId, int pageId, Long userId) {
+	public Client(String sessionId, int pageId, Long userId, UserDao dao) {
 		this.sessionId = sessionId;
 		this.pageId = pageId;
-		this.user = getBean(UserDao.class).get(userId);
+		this.user = dao.get(userId);
 		this.connectedSince = new Date();
 		uid = UUID.randomUUID().toString();
 	}
 
+	public Client(org.apache.openmeetings.db.entity.room.Client rcl, UserDao dao) {
+		this.sessionId = UUID.randomUUID().toString();
+		this.pageId = 0;
+		this.user = dao.get(rcl.getUserId());
+		this.connectedSince = new Date();
+		uid = rcl.getPublicSID();
+		this.roomId = rcl.getRoomId();
+	}
+
 	public String getSessionId() {
 		return sessionId;
 	}

Modified: openmeetings/application/trunk/openmeetings-util/src/main/java/org/apache/openmeetings/util/message/RoomMessage.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-util/src/main/java/org/apache/openmeetings/util/message/RoomMessage.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-util/src/main/java/org/apache/openmeetings/util/message/RoomMessage.java (original)
+++ openmeetings/application/trunk/openmeetings-util/src/main/java/org/apache/openmeetings/util/message/RoomMessage.java Thu Feb 16 04:15:27 2017
@@ -60,7 +60,7 @@ public class RoomMessage implements IWeb
 		this.type = type;
 		this.uid = UUID.randomUUID().toString();
 	}
-	
+
 	public Date getTimestamp() {
 		return timestamp;
 	}

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/admin/connection/ConnectionsPanel.java Thu Feb 16 04:15:27 2017
@@ -127,12 +127,12 @@ public class ConnectionsPanel extends Ad
 		};
 		add(container.add(dataView).setOutputMarkupId(true), details.setVisible(false).setOutputMarkupPlaceholderTag(true));
 		
-		SearchableDataProvider<org.apache.openmeetings.web.app.Client> sdpWeb = new SearchableDataProvider<org.apache.openmeetings.web.app.Client>(null) {
+		SearchableDataProvider<org.apache.openmeetings.db.entity.basic.Client> sdpWeb = new SearchableDataProvider<org.apache.openmeetings.db.entity.basic.Client>(null) {
 			private static final long serialVersionUID = 1L;
 			
 			@Override
-			public Iterator<? extends org.apache.openmeetings.web.app.Client> iterator(long first, long count) {
-				List<org.apache.openmeetings.web.app.Client> l = new ArrayList<org.apache.openmeetings.web.app.Client>(Application.getClients());
+			public Iterator<? extends org.apache.openmeetings.db.entity.basic.Client> iterator(long first, long count) {
+				List<org.apache.openmeetings.db.entity.basic.Client> l = new ArrayList<org.apache.openmeetings.db.entity.basic.Client>(Application.getClients());
 				return l.subList((int)Math.max(0, first), (int)Math.min(first + count, l.size())).iterator();
 			}
 			
@@ -143,12 +143,12 @@ public class ConnectionsPanel extends Ad
 		};
 		
 		final WebMarkupContainer containerWeb = new WebMarkupContainer("containerWeb");
-		SearchableDataView<org.apache.openmeetings.web.app.Client> dataViewWeb = new SearchableDataView<org.apache.openmeetings.web.app.Client>("clientListWeb", sdpWeb) {
+		SearchableDataView<org.apache.openmeetings.db.entity.basic.Client> dataViewWeb = new SearchableDataView<org.apache.openmeetings.db.entity.basic.Client>("clientListWeb", sdpWeb) {
 			private static final long serialVersionUID = 1L;
 
 			@Override
-			protected void populateItem(final Item<org.apache.openmeetings.web.app.Client> item) {
-				org.apache.openmeetings.web.app.Client c = item.getModelObject();
+			protected void populateItem(final Item<org.apache.openmeetings.db.entity.basic.Client> item) {
+				org.apache.openmeetings.db.entity.basic.Client c = item.getModelObject();
 				item.add(new Label("id", ""));
 				item.add(new Label("login", c.getUser().getLogin()));
 				item.add(new Label("since", c.getConnectedSince()));
@@ -158,7 +158,7 @@ public class ConnectionsPanel extends Ad
 
 					@Override
 					protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
-						org.apache.openmeetings.web.app.Client c = item.getModelObject();
+						org.apache.openmeetings.db.entity.basic.Client c = item.getModelObject();
 						getBean(IUserService.class).kickUserBySessionId(getSid(), c.getUserId(), c.getSessionId());
 						target.add(containerWeb, details.setVisible(false));
 					}
@@ -168,9 +168,9 @@ public class ConnectionsPanel extends Ad
 
 					@Override
 					protected void onEvent(AjaxRequestTarget target) {
-						Field[] ff = org.apache.openmeetings.web.app.Client.class.getDeclaredFields();
+						Field[] ff = org.apache.openmeetings.db.entity.basic.Client.class.getDeclaredFields();
 						RepeatingView lines = new RepeatingView("line");
-						org.apache.openmeetings.web.app.Client c = item.getModelObject();
+						org.apache.openmeetings.db.entity.basic.Client c = item.getModelObject();
 						for (Field f : ff) {
 							int mod = f.getModifiers();
 							if (Modifier.isStatic(mod) || Modifier.isTransient(mod)) {

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/Application.java Thu Feb 16 04:15:27 2017
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.web.app;
 
+import static org.apache.openmeetings.core.util.WebSocketHelper.sendRoom;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.wicketApplicationName;
 import static org.apache.openmeetings.web.pages.HashPage.INVITATION_HASH;
@@ -40,9 +41,13 @@ import java.util.concurrent.ConcurrentHa
 import org.apache.openmeetings.IApplication;
 import org.apache.openmeetings.core.remote.MainService;
 import org.apache.openmeetings.core.remote.red5.ScopeApplicationAdapter;
+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.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.record.Recording;
 import org.apache.openmeetings.db.entity.room.Invitation;
 import org.apache.openmeetings.db.entity.room.Room;
@@ -50,8 +55,7 @@ import org.apache.openmeetings.db.entity
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.entity.user.User.Type;
 import org.apache.openmeetings.util.InitializationContainer;
-import org.apache.openmeetings.web.app.Client.Activity;
-import org.apache.openmeetings.web.app.Client.Pod;
+import org.apache.openmeetings.util.message.RoomMessage;
 import org.apache.openmeetings.web.pages.AccessDeniedPage;
 import org.apache.openmeetings.web.pages.ActivatePage;
 import org.apache.openmeetings.web.pages.HashPage;
@@ -215,8 +219,26 @@ public class Application extends Authent
 		ONLINE_USERS.put(c.getUid(), c);
 	}
 
-	public static void removeOnlineUser(Client c) {
+	public static void exitRoom(Client c) {
+		Long roomId = c.getRoomId();
+		removeUserFromRoom(c);
+		if (roomId != null) {
+			sendRoom(new RoomMessage(roomId, c.getUserId(), RoomMessage.Type.roomExit));
+		}
+	}
+
+	@Override
+	public void exit(String uid) {
+		if (uid != null) {
+			exit(ONLINE_USERS.get(uid));
+		}
+	}
+
+	public static void exit(Client c) {
 		if (c != null) {
+			if (c.getRoomId() != null) {
+				exitRoom(c);
+			}
 			log.debug("Removing online client: {}, room: {}", c.getUid(), c.getRoomId());
 			ONLINE_USERS.remove(c.getUid());
 		}
@@ -227,10 +249,22 @@ public class Application extends Authent
 		if (rcl == null) {
 			return null;
 		}
-		if (!rcl.isScreenClient()) {
+		if (!rcl.isScreenClient() && (!rcl.isMobile() || (rcl.isMobile() && rcl.getUserId() != null))) {
 			Client client = getOnlineClient(rcl.getPublicSID());
 			if (client == null) {
-				return null;
+				if (rcl.isMobile()) {
+					//Mobile client enters the room
+					client = new Client(rcl, getBean(UserDao.class));
+					addOnlineUser(client);
+					if (rcl.getRoomId() != null) {
+						addUserToRoom(client);
+						//FIXME TODO unify this
+						WebSocketHelper.sendRoom(new RoomMessage(client.getRoomId(), client.getUserId(), RoomMessage.Type.roomEnter));
+					}
+					//FIXME TODO rights
+				} else {
+					return null;
+				}
 			}
 			rcl.setIsSuperModerator(client.hasRight(Right.superModerator));
 			rcl.setIsMod(client.hasRight(Right.moderator));
@@ -315,7 +349,7 @@ public class Application extends Authent
 		if (client != null) {
 			if (!INVALID_SESSIONS.containsKey(client.getSessionId())) {
 				INVALID_SESSIONS.put(client.getSessionId(), client);
-				removeOnlineUser(client);
+				exit(client);
 			}
 		}
 	}
@@ -351,6 +385,16 @@ public class Application extends Authent
 		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) {
 		List<Client> clients = new ArrayList<>();
 		if (roomId != null) {

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/WebSession.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/WebSession.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/WebSession.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/WebSession.java Thu Feb 16 04:15:27 2017
@@ -24,13 +24,13 @@ import static org.apache.openmeetings.ut
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DASHBOARD_SHOW_RSS_KEY;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DEFAULT_LANG_KEY;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
+import static org.apache.openmeetings.web.app.Application.exit;
 import static org.apache.openmeetings.web.app.Application.getAuthenticationStrategy;
 import static org.apache.openmeetings.web.app.Application.getBean;
 import static org.apache.openmeetings.web.app.Application.getClientByKeys;
 import static org.apache.openmeetings.web.app.Application.getDashboardContext;
 import static org.apache.openmeetings.web.app.Application.isInvaldSession;
 import static org.apache.openmeetings.web.app.Application.removeInvalidSession;
-import static org.apache.openmeetings.web.app.Application.removeOnlineUser;
 import static org.red5.logging.Red5LoggerFactory.getLogger;
 
 import java.util.Arrays;
@@ -121,7 +121,7 @@ public class WebSession extends Abstract
 
 	@Override
 	public void invalidate() {
-		removeOnlineUser(getClientByKeys(userId, getId()));
+		exit(getClientByKeys(userId, getId()));
 		super.invalidate();
 		userId = null;
 		rights = Collections.unmodifiableSet(Collections.<Right>emptySet());

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/common/MainPanel.java Thu Feb 16 04:15:27 2017
@@ -25,8 +25,8 @@ import static org.apache.openmeetings.ut
 import static org.apache.openmeetings.util.OpenmeetingsVariables.LEVEL_USER;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
 import static org.apache.openmeetings.web.app.Application.addOnlineUser;
+import static org.apache.openmeetings.web.app.Application.exit;
 import static org.apache.openmeetings.web.app.Application.getBean;
-import static org.apache.openmeetings.web.app.Application.removeOnlineUser;
 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;
@@ -36,23 +36,23 @@ import static org.apache.openmeetings.we
 import static org.apache.openmeetings.web.util.OmUrlFragment.getPanel;
 import static org.apache.wicket.ajax.attributes.CallbackParameter.explicit;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.dao.basic.NavigationDao;
+import org.apache.openmeetings.db.dao.user.UserDao;
+import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.basic.Naviglobal;
 import org.apache.openmeetings.db.entity.basic.Navimain;
 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.Client;
 import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.common.menu.MainMenuItem;
 import org.apache.openmeetings.web.common.menu.MenuItem;
 import org.apache.openmeetings.web.common.menu.MenuPanel;
-import org.apache.openmeetings.web.room.menu.RoomMenuPanel;
 import org.apache.openmeetings.web.user.AboutDialog;
 import org.apache.openmeetings.web.user.MessageDialog;
 import org.apache.openmeetings.web.user.UserInfoDialog;
@@ -78,8 +78,6 @@ import org.apache.wicket.markup.html.pan
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.CompoundPropertyModel;
 import org.apache.wicket.protocol.http.request.WebClientInfo;
-import org.apache.wicket.protocol.ws.WebSocketSettings;
-import org.apache.wicket.protocol.ws.api.IWebSocketConnection;
 import org.apache.wicket.protocol.ws.api.WebSocketBehavior;
 import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler;
 import org.apache.wicket.protocol.ws.api.message.AbortedMessage;
@@ -87,9 +85,6 @@ import org.apache.wicket.protocol.ws.api
 import org.apache.wicket.protocol.ws.api.message.ClosedMessage;
 import org.apache.wicket.protocol.ws.api.message.ConnectedMessage;
 import org.apache.wicket.protocol.ws.api.message.TextMessage;
-import org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry;
-import org.apache.wicket.protocol.ws.api.registry.PageIdKey;
-import org.apache.wicket.protocol.ws.concurrent.Executor;
 import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.resource.JavaScriptResourceReference;
 import org.apache.wicket.util.time.Duration;
@@ -119,28 +114,7 @@ public class MainPanel extends Panel {
 
 		@Override
 		protected void onTimer(AjaxRequestTarget target) {
-			if (client != null) {
-				WebSocketSettings settings = WebSocketSettings.Holder.get(Application.get());
-				IWebSocketConnectionRegistry reg = settings.getConnectionRegistry();
-				Executor executor = settings.getWebSocketPushMessageExecutor();
-				try {
-					final IWebSocketConnection wsConnection = reg.getConnection(Application.get(), client.getSessionId(), new PageIdKey(client.getPageId()));
-					if (wsConnection != null) {
-						executor.run(new Runnable() {
-							@Override
-							public void run() {
-								try {
-									wsConnection.sendMessage(new byte[1], 0, 1);
-								} catch (IOException e) {
-									log.error("Error while sending ping message to room", e);
-								}
-							}
-						});
-					}
-				} catch (Exception e) {
-					log.error("Error preparing executor", e);
-				}
-			}
+			WebSocketHelper.sendClient(client, new byte[1]);
 		}
 	};
 	private final ExtendedClientProperties extProps = new ExtendedClientProperties();
@@ -249,7 +223,7 @@ public class MainPanel extends Panel {
 			@Override
 			protected void onConnect(ConnectedMessage msg) {
 				super.onConnect(msg);
-				client = new Client(getSession().getId(), msg.getKey().hashCode(), getUserId());
+				client = new Client(getSession().getId(), msg.getKey().hashCode(), getUserId(), getBean(UserDao.class));
 				addOnlineUser(client);
 				log.debug("WebSocketBehavior::onConnect [uid: {}, session: {}, key: {}]", client.getUid(), msg.getSessionId(), msg.getKey());
 			}
@@ -279,11 +253,8 @@ public class MainPanel extends Panel {
 
 			private void closeHandler(AbstractClientMessage msg) {
 				//no chance to stop pingTimer here :(
-				if (client != null && client.getRoomId() != null) {
-					RoomMenuPanel.roomExit(client);
-				}
 				log.debug("WebSocketBehavior::closeHandler [uid: {}, session: {}, key: {}]", client.getUid(), msg.getSessionId(), msg.getKey());
-				removeOnlineUser(client);
+				exit(client);
 				client = null;
 			}
 		});

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomBroadcaster.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomBroadcaster.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomBroadcaster.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomBroadcaster.java Thu Feb 16 04:15:27 2017
@@ -50,7 +50,7 @@ public class RoomBroadcaster {
 		sa.sendToScope(roomId, method, obj);
 	}
 
-	public static void sendUpdatedClient(org.apache.openmeetings.web.app.Client client) {
+	public static void sendUpdatedClient(org.apache.openmeetings.db.entity.basic.Client client) {
 		org.apache.openmeetings.db.entity.room.Client rcl = Application.get().updateClient(getClient(client.getUid()));
 		log.debug("-----------  sendUpdatedClient ");
 

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/RoomPanel.java Thu Feb 16 04:15:27 2017
@@ -35,8 +35,9 @@ import java.util.UUID;
 import org.apache.directory.api.util.Strings;
 import org.apache.openmeetings.core.remote.ConferenceLibrary;
 import org.apache.openmeetings.core.remote.red5.ScopeApplicationAdapter;
+import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.dao.calendar.AppointmentDao;
-import org.apache.openmeetings.db.dao.user.UserDao;
+import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.calendar.Appointment;
 import org.apache.openmeetings.db.entity.calendar.MeetingMember;
 import org.apache.openmeetings.db.entity.file.FileItem;
@@ -52,7 +53,6 @@ import org.apache.openmeetings.util.mess
 import org.apache.openmeetings.util.message.RoomMessage.Type;
 import org.apache.openmeetings.util.message.TextRoomMessage;
 import org.apache.openmeetings.web.app.Application;
-import org.apache.openmeetings.web.app.Client;
 import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.common.BasePanel;
 import org.apache.openmeetings.web.room.activities.ActivitiesPanel;
@@ -62,7 +62,6 @@ import org.apache.openmeetings.web.room.
 import org.apache.wicket.Component;
 import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.json.JSONObject;
 import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
 import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
 import org.apache.wicket.event.IEvent;
@@ -71,15 +70,11 @@ import org.apache.wicket.markup.head.Jav
 import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
 import org.apache.wicket.markup.head.PriorityHeaderItem;
 import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.protocol.ws.WebSocketSettings;
-import org.apache.wicket.protocol.ws.api.IWebSocketConnection;
 import org.apache.wicket.protocol.ws.api.event.WebSocketPushPayload;
-import org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry;
-import org.apache.wicket.protocol.ws.api.registry.PageIdKey;
-import org.apache.wicket.protocol.ws.concurrent.Executor;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.JavaScriptResourceReference;
 import org.apache.wicket.request.resource.ResourceReference;
+import org.json.JSONObject;
 import org.red5.logging.Red5LoggerFactory;
 import org.slf4j.Logger;
 import org.wicketstuff.whiteboard.WhiteboardBehavior;
@@ -133,7 +128,7 @@ public class RoomPanel extends BasePanel
 			} catch (MalformedURLException e) {
 				log.error("Error while constructing room parameters", e);
 			}
-			broadcast(new RoomMessage(r.getId(), getUserId(), RoomMessage.Type.roomEnter));
+			WebSocketHelper.sendRoom(new RoomMessage(r.getId(), getUserId(), RoomMessage.Type.roomEnter));
 			getMainPanel().getChat().roomEnter(r, target);
 			if (r.isFilesOpened()) {
 				sidebar.setFilesActive(target);
@@ -235,7 +230,7 @@ public class RoomPanel extends BasePanel
 				allowed = r.getIspublic() || (r.getOwnerId() != null && r.getOwnerId().equals(getUserId()));
 				log.debug("public ? " + r.getIspublic() + ", ownedId ? " + r.getOwnerId() + " " + allowed);
 				if (!allowed) {
-					User u = getBean(UserDao.class).get(getUserId());
+					User u = getClient().getUser();
 					for (RoomGroup ro : r.getRoomGroups()) {
 						for (GroupUser ou : u.getGroupUsers()) {
 							if (ro.getGroup().getId().equals(ou.getGroup().getId())) {
@@ -286,7 +281,7 @@ public class RoomPanel extends BasePanel
 
 			@Override
 			public void onClose(IPartialPageRequestHandler handler, DialogButton button) {
-				menu.exit(handler, true);
+				menu.exit(handler);
 			}
 		});
 	}
@@ -421,8 +416,8 @@ public class RoomPanel extends BasePanel
 			if (soap != null && soap.isModerator()) {
 				c.getRights().add(Right.superModerator);
 			} else {
-				User u = getBean(UserDao.class).get(getUserId());
-				Right rr = AuthLevelUtil.getRoomRight(u, r, r.isAppointment() ? getBean(AppointmentDao.class).getByRoom(r.getId()) : null, getRoomClients(r.getId()).size());
+				//FIXME TODO !!! c.getUser != getUserId
+				Right rr = AuthLevelUtil.getRoomRight(c.getUser(), r, r.isAppointment() ? getBean(AppointmentDao.class).getByRoom(r.getId()) : null, getRoomClients(r.getId()).size());
 				if (rr != null) {
 					c.getRights().add(rr);
 				}
@@ -430,27 +425,6 @@ public class RoomPanel extends BasePanel
 		}
 	}
 
-	public static void broadcast(final RoomMessage m) {
-		WebSocketSettings settings = WebSocketSettings.Holder.get(Application.get());
-		IWebSocketConnectionRegistry reg = settings.getConnectionRegistry();
-		Executor executor = settings.getWebSocketPushMessageExecutor();
-		for (Client c : getRoomClients(m.getRoomId())) {
-			try {
-				final IWebSocketConnection wsConnection = reg.getConnection(Application.get(), c.getSessionId(), new PageIdKey(c.getPageId()));
-				if (wsConnection != null) {
-					executor.run(new Runnable() {
-						@Override
-						public void run() {
-							wsConnection.sendMessage(m);
-						}
-					});
-				}
-			} catch (Exception e) {
-				log.error("Error while broadcasting message to room", e);
-			}
-		}
-	}
-
 	public static boolean isModerator(long userId, long roomId) {
 		return hasRight(userId, roomId, Right.moderator);
 	}
@@ -464,17 +438,6 @@ public class RoomPanel extends BasePanel
 		return false;
 	}
 
-	public static void sendRoom(long roomId, String msg) {
-		IWebSocketConnectionRegistry reg = WebSocketSettings.Holder.get(Application.get()).getConnectionRegistry();
-		for (Client c : getRoomClients(roomId)) {
-			try {
-				reg.getConnection(Application.get(), c.getSessionId(), new PageIdKey(c.getPageId())).sendMessage(msg);
-			} catch (Exception e) {
-				log.error("Error while sending message to room", e);
-			}
-		}
-	}
-
 	@Override
 	public BasePanel onMenuPanelLoad(IPartialPageRequestHandler handler) {
 		getBasePage().getHeader().setVisible(false);
@@ -506,7 +469,7 @@ public class RoomPanel extends BasePanel
 			getMainPanel().getChat().toggle(handler, true);
 		}
 		handler.appendJavaScript("if (typeof roomUnload == 'function') { roomUnload(); }");
-		RoomMenuPanel.roomExit(getClient());
+		Application.exitRoom(getClient());
 		getMainPanel().getChat().roomExit(r, handler);
 	}
 
@@ -560,7 +523,7 @@ public class RoomPanel extends BasePanel
 				break;
 		}
 		if (reqType != null) {
-			RoomPanel.broadcast(new TextRoomMessage(getRoom().getId(), getUserId(), reqType, getClient().getUid()));
+			WebSocketHelper.sendRoom(new TextRoomMessage(getRoom().getId(), getUserId(), reqType, getClient().getUid()));
 		}
 	}
 
@@ -585,11 +548,11 @@ public class RoomPanel extends BasePanel
 	}
 
 	public void kickUser(AjaxRequestTarget target, Client client) {
-		RoomPanel.broadcast(new TextRoomMessage(client.getRoomId(), client.getUserId(), Type.kick, client.getUid()));
+		WebSocketHelper.sendRoom(new TextRoomMessage(client.getRoomId(), client.getUserId(), Type.kick, client.getUid()));
 	}
 
 	public void broadcast(AjaxRequestTarget target, Client client) {
-		broadcast(new RoomMessage(getRoom().getId(), getUserId(), RoomMessage.Type.rightUpdated));
+		WebSocketHelper.sendRoom(new RoomMessage(getRoom().getId(), getUserId(), RoomMessage.Type.rightUpdated));
 		RoomBroadcaster.sendUpdatedClient(client);
 	}
 

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/activities/ActivitiesPanel.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/activities/ActivitiesPanel.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/activities/ActivitiesPanel.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/activities/ActivitiesPanel.java Thu Feb 16 04:15:27 2017
@@ -18,11 +18,11 @@
  */
 package org.apache.openmeetings.web.room.activities;
 
+import static org.apache.openmeetings.core.util.WebSocketHelper.sendRoom;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
 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.room.RoomPanel.broadcast;
 import static org.apache.openmeetings.web.util.CallbackFunctionHelper.getNamedFunction;
 import static org.apache.wicket.ajax.attributes.CallbackParameter.explicit;
 
@@ -33,12 +33,12 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 
 import org.apache.openmeetings.db.dao.user.UserDao;
+import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.room.Room.Right;
 import org.apache.openmeetings.db.entity.room.Room.RoomElement;
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.util.message.RoomMessage;
 import org.apache.openmeetings.util.message.TextRoomMessage;
-import org.apache.openmeetings.web.app.Client;
 import org.apache.openmeetings.web.common.BasePanel;
 import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.wicket.Component;
@@ -82,7 +82,7 @@ public class ActivitiesPanel extends Bas
 		@Override
 		protected void respond(AjaxRequestTarget target) {
 			try {
-				String id = getRequest().getRequestParameters().getParameterValue(PARAM_ID).toString(); 
+				String id = getRequest().getRequestParameters().getParameterValue(PARAM_ID).toString();
 				long roomId = getRequest().getRequestParameters().getParameterValue(PARAM_ROOM_ID).toLong();
 				assert(room.getRoom().getId().equals(roomId));
 				Action action = Action.valueOf(getRequest().getRequestParameters().getParameterValue(ACTION).toString());
@@ -94,7 +94,7 @@ public class ActivitiesPanel extends Bas
 							break;
 						case decline:
 							if (room.getClient().hasRight(Right.moderator)) {
-								broadcast(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
+								sendRoom(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
 							}
 							break;
 						case accept:
@@ -102,35 +102,35 @@ public class ActivitiesPanel extends Bas
 							if (room.getClient().hasRight(Right.moderator) && client != null && roomId == client.getRoomId()) {
 								switch (a.getType()) {
 									case reqRightModerator:
-										broadcast(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
+										sendRoom(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
 										room.allowRight(target, client, Right.moderator);
 										break;
 									case reqRightAv:
-										broadcast(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
+										sendRoom(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
 										room.allowRight(target, client, Right.audio, Right.video);
 										break;
 									case reqRightWb:
-										broadcast(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
+										sendRoom(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
 										room.allowRight(target, client, Right.whiteBoard);
 										break;
 									case reqRightShare:
-										broadcast(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
+										sendRoom(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
 										room.allowRight(target, client, Right.share);
 										break;
 									case reqRightRemote:
-										broadcast(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
+										sendRoom(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
 										room.allowRight(target, client, Right.remoteControl);
 										break;
 									case reqRightA:
-										broadcast(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
+										sendRoom(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
 										room.allowRight(target, client, Right.audio);
 										break;
 									case reqRightMute:
-										broadcast(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
+										sendRoom(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
 										room.allowRight(target, client, Right.mute);
 										break;
 									case reqRightExclusive:
-										broadcast(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
+										sendRoom(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.activityRemove, id));
 										room.allowRight(target, client, Right.exclusive);
 										break;
 									default:
@@ -146,7 +146,7 @@ public class ActivitiesPanel extends Bas
 				log.error("Unexpected exception while processing activity action", e);
 			}
 		}
-		
+
 		@Override
 		public void renderHead(Component component, IHeaderResponse response) {
 			super.renderHead(component, response);
@@ -222,7 +222,7 @@ public class ActivitiesPanel extends Bas
 			item.add(accept, decline, new Label("text", text));
 			item.add(AttributeAppender.append("class", getClass(a)));
 		}
-		
+
 		private String getClass(Activity a) {
 			String cls = "ui-state-default";
 			switch (a.getType()) {
@@ -262,7 +262,7 @@ public class ActivitiesPanel extends Bas
 			handler.add(container);
 		}
 	}
-	
+
 	public ActivitiesPanel(String id, RoomPanel room) {
 		super(id);
 		this.room = room;
@@ -272,7 +272,7 @@ public class ActivitiesPanel extends Bas
 		add(container.add(lv).setOutputMarkupPlaceholderTag(true));
 		add(action);
 	}
-	
+
 	@Override
 	public void renderHead(IHeaderResponse response) {
 		super.renderHead(response);

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/RoomMenuPanel.java Thu Feb 16 04:15:27 2017
@@ -18,10 +18,10 @@
  */
 package org.apache.openmeetings.web.room.menu;
 
+import static org.apache.openmeetings.core.util.WebSocketHelper.sendRoom;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_APPLICATION_BASE_URL;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_REDIRECT_URL_FOR_EXTERNAL_KEY;
 import static org.apache.openmeetings.web.app.Application.getBean;
-import static org.apache.openmeetings.web.app.Application.removeUserFromRoom;
 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;
@@ -33,7 +33,6 @@ import org.apache.commons.lang3.time.Fas
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.room.PollDao;
 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.RoomElement;
 import org.apache.openmeetings.db.entity.room.RoomPoll;
@@ -42,7 +41,6 @@ import org.apache.openmeetings.db.entity
 import org.apache.openmeetings.util.message.RoomMessage;
 import org.apache.openmeetings.util.message.TextRoomMessage;
 import org.apache.openmeetings.web.app.Application;
-import org.apache.openmeetings.web.app.Client;
 import org.apache.openmeetings.web.app.WebSession;
 import org.apache.openmeetings.web.common.ImagePanel;
 import org.apache.openmeetings.web.common.InvitationDialog;
@@ -128,7 +126,7 @@ public class RoomMenuPanel extends Panel
 
 		@Override
 		public void onClick(AjaxRequestTarget target) {
-			RoomPanel.broadcast(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.requestRightWb, room.getClient().getUid()));
+			sendRoom(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.requestRightWb, room.getClient().getUid()));
 		}
 	};
 	private final RoomMenuItem applyAvMenuItem = new RoomMenuItem(Application.getString(786), Application.getString(1482), false) {
@@ -136,7 +134,7 @@ public class RoomMenuPanel extends Panel
 
 		@Override
 		public void onClick(AjaxRequestTarget target) {
-			RoomPanel.broadcast(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.requestRightAv, room.getClient().getUid()));
+			sendRoom(new TextRoomMessage(room.getRoom().getId(), getUserId(), RoomMessage.Type.requestRightAv, room.getClient().getUid()));
 		}
 	};
 	private final RoomMenuItem pollCreateMenuItem = new RoomMenuItem(Application.getString(24), Application.getString(1483), false) {
@@ -276,7 +274,7 @@ public class RoomMenuPanel extends Panel
 		Room r = room.getRoom();
 		PollDao pollDao = getBean(PollDao.class);
 		boolean pollExists = pollDao.hasPoll(r.getId());
-		User u = getBean(UserDao.class).get(getUserId());
+		User u = room.getClient().getUser();
 		boolean notExternalUser = u.getType() != User.Type.external && u.getType() != User.Type.contact;
 		exitMenuItem.setEnabled(notExternalUser);//TODO check this
 		filesMenu.setEnabled(room.getSidebar().isShowFiles());
@@ -332,12 +330,8 @@ public class RoomMenuPanel extends Panel
 	}
 
 	public void exit(IPartialPageRequestHandler handler) {
-		exit(handler, true);
-	}
-
-	public void exit(IPartialPageRequestHandler handler, boolean broadcast) {
 		if (WebSession.getRights().contains(User.Right.Dashboard)) {
-			roomExit(room.getClient(), broadcast);
+			Application.exitRoom(room.getClient());
 			room.getMainPanel().updateContents(ROOMS_PUBLIC, handler);
 		} else {
 			String url = getBean(ConfigurationDao.class).getConfValue(CONFIG_REDIRECT_URL_FOR_EXTERNAL_KEY, String.class, "");
@@ -347,17 +341,5 @@ public class RoomMenuPanel extends Panel
 			throw new RedirectToUrlException(url);
 		}
 	}
-
-	public static void roomExit(Client c) {
-		roomExit(c, true);
-	}
-
-	public static void roomExit(Client c, boolean broadcast) {
-		Long roomId = c.getRoomId();
-		removeUserFromRoom(c);
-		if (broadcast && roomId != null) {
-			RoomPanel.broadcast(new RoomMessage(roomId, c.getUserId(), RoomMessage.Type.roomExit));
-		}
-	}
 }
 

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/StartSharingButton.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/StartSharingButton.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/StartSharingButton.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/menu/StartSharingButton.java Thu Feb 16 04:15:27 2017
@@ -60,7 +60,7 @@ public class StartSharingButton extends
 	private static final String CDATA_BEGIN = "<![CDATA[";
 	private static final String CDATA_END = "]]>";
 	private final AjaxDownload download;
-	private final org.apache.openmeetings.web.app.Client c;
+	private final org.apache.openmeetings.db.entity.basic.Client c;
 	private final ExtendedClientProperties extProps;
 	private enum Protocol {
 		rtmp
@@ -69,7 +69,7 @@ public class StartSharingButton extends
 		, rtmpt
 	}
 
-	public StartSharingButton(String id, org.apache.openmeetings.web.app.Client c, final ExtendedClientProperties extProps) {
+	public StartSharingButton(String id, org.apache.openmeetings.db.entity.basic.Client c, final ExtendedClientProperties extProps) {
 		super(id);
 		this.c = c;
 		this.extProps = extProps;

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/poll/CreatePollDialog.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/poll/CreatePollDialog.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/poll/CreatePollDialog.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/poll/CreatePollDialog.java Thu Feb 16 04:15:27 2017
@@ -18,6 +18,7 @@
  */
 package org.apache.openmeetings.web.room.poll;
 
+import static org.apache.openmeetings.core.util.WebSocketHelper.sendRoom;
 import static org.apache.openmeetings.web.app.Application.getBean;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 
@@ -31,7 +32,6 @@ import org.apache.openmeetings.db.entity
 import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.util.message.RoomMessage;
 import org.apache.openmeetings.web.app.Application;
-import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.html.form.ChoiceRenderer;
 import org.apache.wicket.markup.html.form.DropDownChoice;
@@ -70,12 +70,12 @@ public class CreatePollDialog extends Ab
 		form.setModelObject(p);
 		target.add(form);
 	}
-	
+
 	@Override
 	protected List<DialogButton> getButtons() {
 		return Arrays.asList(create, cancel);
 	}
-	
+
 	@Override
 	public DialogButton getSubmitButton() {
 		return create;
@@ -96,12 +96,12 @@ public class CreatePollDialog extends Ab
 		PollDao dao = getBean(PollDao.class);
 		dao.close(roomId);
 		dao.update(form.getModelObject());
-		RoomPanel.broadcast(new RoomMessage(roomId, getUserId(), RoomMessage.Type.pollCreated));
+		sendRoom(new RoomMessage(roomId, getUserId(), RoomMessage.Type.pollCreated));
 	}
 
 	private class PollForm extends Form<RoomPoll> {
 		private static final long serialVersionUID = 1L;
-		
+
 		public PollForm(String id, IModel<RoomPoll> model) {
 			super(id, model);
 			add(new RequiredTextField<String>("name").setLabel(Model.of(Application.getString(1410))));

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/ClientIconsPanel.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/ClientIconsPanel.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/ClientIconsPanel.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/ClientIconsPanel.java Thu Feb 16 04:15:27 2017
@@ -18,7 +18,7 @@
  */
 package org.apache.openmeetings.web.room.sidebar;
 
-import org.apache.openmeetings.web.app.Client;
+import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.openmeetings.web.room.sidebar.icon.right.AudioRightIcon;
 import org.apache.openmeetings.web.room.sidebar.icon.right.ExclusiveRightIcon;

Modified: openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomClientPanel.java
URL: http://svn.apache.org/viewvc/openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomClientPanel.java?rev=1783168&r1=1783167&r2=1783168&view=diff
==============================================================================
--- openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomClientPanel.java (original)
+++ openmeetings/application/trunk/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/sidebar/RoomClientPanel.java Thu Feb 16 04:15:27 2017
@@ -20,10 +20,10 @@ package org.apache.openmeetings.web.room
 
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 
+import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.room.Room.Right;
 import org.apache.openmeetings.db.entity.room.Room.RoomElement;
 import org.apache.openmeetings.db.entity.user.User;
-import org.apache.openmeetings.web.app.Client;
 import org.apache.openmeetings.web.room.RoomPanel;
 import org.apache.openmeetings.web.room.sidebar.icon.KickIcon;
 import org.apache.openmeetings.web.room.sidebar.icon.RefreshIcon;