Java tutorial
/* * 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.remote.red5; import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import org.apache.openmeetings.core.data.conference.RoomManager; import org.apache.openmeetings.core.data.whiteboard.EmoticonsManager; import org.apache.openmeetings.core.data.whiteboard.WhiteboardManager; 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.db.dao.basic.ConfigurationDao; import org.apache.openmeetings.db.dao.calendar.AppointmentDao; import org.apache.openmeetings.db.dao.label.LabelDao; 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.server.ServerDao; import org.apache.openmeetings.db.dao.server.SessiondataDao; import org.apache.openmeetings.db.dao.user.UserDao; import org.apache.openmeetings.db.dto.room.BrowserStatus; import org.apache.openmeetings.db.dto.room.RoomStatus; import org.apache.openmeetings.db.entity.log.ConferenceLog; import org.apache.openmeetings.db.entity.room.Client; import org.apache.openmeetings.db.entity.room.Room; import org.apache.openmeetings.db.entity.server.Server; import org.apache.openmeetings.db.entity.user.User; import org.apache.openmeetings.db.util.AuthLevelUtil; import org.apache.openmeetings.util.CalendarPatterns; import org.apache.openmeetings.util.InitializationContainer; import org.apache.openmeetings.util.OmFileHelper; import org.apache.openmeetings.util.OpenmeetingsVariables; import org.apache.openmeetings.util.Version; import org.apache.wicket.util.string.StringValue; import org.apache.wicket.util.string.Strings; import org.red5.logging.Red5LoggerFactory; import org.red5.server.adapter.ApplicationAdapter; import org.red5.server.api.IClient; import org.red5.server.api.IConnection; import org.red5.server.api.Red5; import org.red5.server.api.scope.IBasicScope; import org.red5.server.api.scope.IBroadcastScope; import org.red5.server.api.scope.IScope; import org.red5.server.api.scope.ScopeType; import org.red5.server.api.service.IPendingServiceCall; import org.red5.server.api.service.IPendingServiceCallback; import org.red5.server.api.service.IServiceCapableConnection; import org.red5.server.api.stream.IBroadcastStream; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; public class ScopeApplicationAdapter extends ApplicationAdapter implements IPendingServiceCallback { private static final Logger log = Red5LoggerFactory.getLogger(ScopeApplicationAdapter.class, webAppRootKey); private static final String SECURITY_CODE_PARAM = "securityCode"; private static final String NATIVE_SSL_PARAM = "nativeSsl"; @Autowired private ISessionManager sessionManager; @Autowired private EmoticonsManager emoticonsManager; @Autowired private WhiteBoardService whiteBoardService; @Autowired private WhiteboardManager whiteboardManagement; @Autowired private RecordingService recordingService; @Autowired private ConfigurationDao configurationDao; @Autowired private AppointmentDao appointmentDao; @Autowired private SessiondataDao sessiondataDao; @Autowired private RoomManager roomManager; @Autowired private ConferenceLogDao conferenceLogDao; @Autowired private UserDao userDao; @Autowired private RoomDao roomDao; @Autowired private RecordingDao recordingDao; @Autowired private ServerDao serverDao; @Autowired private SipDao sipDao; private static AtomicLong broadCastCounter = new AtomicLong(0); @Override public void resultReceived(IPendingServiceCall arg0) { if (log.isTraceEnabled()) { log.trace("resultReceived:: {}", arg0); } } @Override public boolean appStart(IScope scope) { try { OmFileHelper.setOmHome(scope.getResource("/").getFile()); LabelDao.initLanguageMap(); log.debug("webAppPath : " + OmFileHelper.getOmHome()); // Only load this Class one time Initially this value might by empty, because the DB is empty yet getCryptKey(); // init your handler here // The scheduled Jobs did go into the Spring-Managed Beans, see schedulerJobs.service.xml // Spring Definition does not work here, its too early, Instance is not set yet emoticonsManager.loadEmot(); 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 } catch (Exception err) { log.error("[appStart]", err); } return true; } @SuppressWarnings("unchecked") private static Map<String, Object> getConnParams(Object[] params) { if (params != null && params.length > 0) { return (Map<String, Object>) params[0]; } return new HashMap<>(); } @Override public boolean roomConnect(IConnection conn, Object[] params) { log.debug("roomConnect : "); IServiceCapableConnection service = (IServiceCapableConnection) conn; String streamId = conn.getClient().getId(); log.debug("### Client connected to OpenMeetings, register Client StreamId: " + streamId + " scope " + conn.getScope().getName()); // Set StreamId in Client service.invoke("setId", new Object[] { streamId }, this); Map<String, Object> map = conn.getConnectParams(); String swfURL = map.containsKey("swfUrl") ? (String) map.get("swfUrl") : ""; String tcUrl = map.containsKey("tcUrl") ? (String) map.get("tcUrl") : ""; Map<String, Object> connParams = getConnParams(params); String uid = (String) connParams.get("uid"); String securityCode = (String) connParams.get(SECURITY_CODE_PARAM); if (!Strings.isEmpty(securityCode)) { //FIXME TODO add better mechanism, this is for external applications like ffmpeg Client parent = sessionManager.getClientByPublicSID(securityCode, null); if (parent == null || !parent.getScope().equals(conn.getScope().getName())) { return rejectClient(); } } if ("networktest".equals(uid)) { return true; } Client parentClient = null; //TODO add similar code for other connections if (map.containsKey("screenClient")) { String parentSid = (String) map.get("parentSid"); parentClient = sessionManager.getClientByPublicSID(parentSid, null); if (parentClient == null) { return rejectClient(); } } Client rcm = new Client(); rcm.setStreamid(conn.getClient().getId()); StringValue scn = StringValue.valueOf(conn.getScope().getName()); rcm.setScope(scn.toString()); long roomId = scn.toLong(Long.MIN_VALUE); if (Long.MIN_VALUE != roomId) { rcm.setRoomId(roomId); } else if (!"hibernate".equals(scn.toString())) { return rejectClient(); } rcm.setUserport(conn.getRemotePort()); rcm.setUserip(conn.getRemoteAddress()); rcm.setSwfurl(swfURL); rcm.setTcUrl(tcUrl); rcm.setNativeSsl(Boolean.TRUE.equals(connParams.get(NATIVE_SSL_PARAM))); rcm.setPublicSID(uid); rcm.setSecurityCode(securityCode); rcm = sessionManager.add(rcm, null); if (rcm == null) { 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")) { //TODO add check for room rights String parentSid = parentClient.getPublicSID(); rcm.setRoomId(Long.valueOf(conn.getScope().getName())); rcm.setScreenClient(true); SessionVariablesUtil.setIsScreenClient(conn.getClient()); rcm.setUserId(parentClient.getUserId()); Long userId = rcm.getUserId(); SessionVariablesUtil.setUserId(conn.getClient(), userId); rcm.setStreamPublishName(parentSid); User u = null; if (userId != null) { long _uid = userId.longValue(); u = userDao.get(_uid < 0 ? -_uid : _uid); } if (u != null) { rcm.setUsername(u.getLogin()); rcm.setFirstname(u.getFirstname()); rcm.setLastname(u.getLastname()); } log.debug("publishName :: " + rcm.getStreamPublishName()); sessionManager.updateClientByStreamId(streamId, rcm, false, null); } // Log the User conferenceLogDao.add(ConferenceLog.Type.clientConnect, rcm.getUserId(), streamId, null, rcm.getUserip(), rcm.getScope()); return true; } public Map<String, String> screenSharerAction(Map<String, Object> map) { Map<String, String> returnMap = new HashMap<String, String>(); try { log.debug("----------- screenSharerAction ENTER"); IConnection current = Red5.getConnectionLocal(); Client client = sessionManager.getClientByStreamId(current.getClient().getId(), null); if (client != null) { boolean changed = false; if (Boolean.parseBoolean("" + map.get("stopStreaming")) && client.isStartStreaming()) { changed = true; client.setStartStreaming(false); //Send message to all users sendMessageToCurrentScope("stopScreenSharingMessage", client, false); returnMap.put("result", "stopSharingOnly"); } if (Boolean.parseBoolean("" + map.get("stopRecording")) && client.getIsRecording()) { changed = true; client.setStartRecording(false); client.setIsRecording(false); returnMap.put("result", "stopRecordingOnly"); //Send message to all users sendMessageToCurrentScope("stopRecordingMessage", client, false); recordingService.stopRecordAndSave(current.getScope(), client, null); } if (Boolean.parseBoolean("" + map.get("stopPublishing")) && client.isScreenPublishStarted()) { 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"); } } } log.debug("----------- screenSharerAction, return: " + returnMap); } catch (Exception err) { log.error("[screenSharerAction]", err); } return returnMap; } public List<Client> checkScreenSharing() { try { IConnection current = Red5.getConnectionLocal(); String streamid = current.getClient().getId(); log.debug("checkScreenSharing -2- " + streamid); List<Client> screenSharerList = new LinkedList<Client>(); Client currentClient = sessionManager.getClientByStreamId(streamid, null); for (Client rcl : sessionManager.getClientListByRoomAll(currentClient.getRoomId())) { if (rcl.isStartStreaming()) { screenSharerList.add(rcl); } } return screenSharerList; } catch (Exception err) { log.error("[checkScreenSharing]", err); } return null; } /** * * @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 { log.debug("----------- setConnectionAsSharingClient"); IConnection current = Red5.getConnectionLocal(); Client client = sessionManager.getClientByStreamId(current.getClient().getId(), null); if (client != null) { boolean startRecording = Boolean.parseBoolean("" + map.get("startRecording")); boolean startStreaming = Boolean.parseBoolean("" + map.get("startStreaming")); boolean startPublishing = Boolean.parseBoolean("" + map.get("startPublishing")) && (0 == sessionManager.getPublishingCount(client.getRoomId())); boolean alreadyStreaming = client.isStartStreaming(); if (startStreaming) { client.setStartStreaming(true); } boolean alreadyRecording = client.isStartRecording(); if (startRecording) { client.setStartRecording(true); } if (startPublishing) { client.setStreamPublishStarted(true); } client.setVX(Double.valueOf("" + map.get("screenX")).intValue()); client.setVY(Double.valueOf("" + map.get("screenY")).intValue()); client.setVWidth(Double.valueOf("" + map.get("screenWidth")).intValue()); client.setVHeight(Double.valueOf("" + map.get("screenHeight")).intValue()); client.setStreamPublishName("" + map.get("publishName")); sessionManager.updateClientByStreamId(current.getClient().getId(), client, false, null); Map<String, Object> returnMap = new HashMap<String, Object>(); returnMap.put("alreadyPublished", false); // if is already started screen sharing, then there is no need // to start it again if (client.isScreenPublishStarted()) { returnMap.put("alreadyPublished", true); } log.debug(String.format("screen x,y,width,height %s,%s,%s,%s", client.getVX(), client.getVY(), client.getVWidth(), client.getVHeight())); if (startStreaming) { if (!alreadyStreaming) { returnMap.put("modus", "startStreaming"); log.debug("start streamPublishStart Is Screen Sharing "); //Send message to all users sendMessageToCurrentScope("newScreenSharing", client, false); } else { log.warn("Streaming is already started for the client id=" + client.getId() + ". Second request is ignored."); } } 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."); } } if (startPublishing) { sendMessageToCurrentScope( "startedPublishing", new Object[] { client, "rtmp://" + map.get("publishingHost") + ":1935/" + map.get("publishingApp") + "/" + map.get("publishingId") }, false, true); returnMap.put("modus", "startPublishing"); } return returnMap; } else { log.error("[setConnectionAsSharingClient] Could not find Screen Sharing Client " + current.getClient().getId()); } } catch (Exception err) { log.error("[setConnectionAsSharingClient]", err); } return null; } public List<Long> listRoomBroadcast() { Set<Long> broadcastList = new HashSet<>(); IConnection current = Red5.getConnectionLocal(); String streamid = current.getClient().getId(); for (IConnection conn : current.getScope().getClientConnections()) { if (conn != null) { Client rcl = sessionManager.getClientByStreamId(conn.getClient().getId(), null); if (rcl == null) { // continue; } else if (rcl.isScreenClient()) { // continue; } else { if (!streamid.equals(rcl.getStreamid())) { // It is not needed to send back // that event to the actuall // Moderator // as it will be already triggered // in the result of this Function // in the Client Long id = Long.valueOf(rcl.getBroadCastID()); if (!broadcastList.contains(id)) { broadcastList.add(id); } } } } } return new ArrayList<Long>(broadcastList); } /** * this function is invoked directly after initial connecting * * @return publicSID of current client */ public String getPublicSID() { log.debug("----------- getPublicSID"); IConnection current = Red5.getConnectionLocal(); Client currentClient = sessionManager.getClientByStreamId(current.getClient().getId(), null); sessionManager.updateClientByStreamId(current.getClient().getId(), currentClient, false, null); return currentClient.getPublicSID(); } /** * this function is invoked after a reconnect * * @param newPublicSID */ public boolean overwritePublicSID(String newPublicSID) { try { log.debug("----------- overwritePublicSID"); IConnection current = Red5.getConnectionLocal(); IClient c = current.getClient(); Client currentClient = sessionManager.getClientByStreamId(c.getId(), null); if (currentClient == null) { return false; } SessionVariablesUtil.initClient(c, newPublicSID); currentClient.setPublicSID(newPublicSID); sessionManager.updateClientByStreamId(c.getId(), currentClient, false, null); return true; } catch (Exception err) { log.error("[overwritePublicSID]", err); } return false; } /** * Logic must be before roomDisconnect cause otherwise you cannot throw a * message to each one * */ @Override public void roomLeave(IClient client, IScope room) { try { log.debug(String.format("roomLeave %s %s %s %s", client.getId(), room.getClients().size(), room.getContextPath(), room.getName())); Client currentClient = sessionManager.getClientByStreamId(client.getId(), null); // The Room Client can be null if the Client left the room by using // logicalRoomLeave if (currentClient != null) { log.debug("currentClient IS NOT NULL"); roomLeaveByScope(currentClient, room, true); } } catch (Exception err) { log.error("[roomLeave]", err); } } /** * 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 "); try { IConnection current = Red5.getConnectionLocal(); String streamid = current.getClient().getId(); log.debug(streamid + " is leaving"); Client currentClient = sessionManager.getClientByStreamId(streamid, null); roomLeaveByScope(currentClient, current.getScope(), true); } catch (Exception err) { log.error("[logicalRoomLeave]", err); } } /** * 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 * to call it with proper values. * * @param client * @param scope */ public void roomLeaveByScope(Client client, IScope scope, boolean removeUserFromSessionList) { try { log.debug("currentClient " + client); Long roomId = client.getRoomId(); // Log the User conferenceLogDao.add(ConferenceLog.Type.roomLeave, client.getUserId(), client.getStreamid(), roomId, client.getUserip(), ""); // Remove User from Sync List's if (roomId != null) { whiteBoardService.removeUserFromAllLists(scope, client); } log.debug("removing Username " + client.getUsername() + " " + client.getConnectedSince() + " streamid: " + client.getStreamid()); // stop and save any recordings if (client.getIsRecording()) { log.debug("*** roomLeave Current Client is Recording - stop that"); if (client.getInterviewPodId() != null) { //interview, TODO need better check _stopInterviewRecording(client, scope); } else { recordingService.stopRecordAndSave(scope, client, null); // set to true and overwrite the default one cause otherwise no // notification is send client.setIsRecording(true); } } // Notify all clients of the same currentScope (room) with domain // and room except the current disconnected cause it could throw an exception log.debug("currentScope " + scope); if (scope != null && scope.getClientConnections() != null) { // Notify Users of the current Scope for (IConnection cons : scope.getClientConnections()) { if (cons != null && cons instanceof IServiceCapableConnection) { log.debug("sending roomDisconnect to {} client id {}", cons, cons.getClient().getId()); Client rcl = sessionManager.getClientByStreamId(cons.getClient().getId(), null); // Check if the Client does still exist on the list if (rcl == null) { 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 log.debug("###########[roomLeaveByScope]"); if (rcl.getIsRecording()) { 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 ((IServiceCapableConnection) cons).invoke("stopStream", new Object[] {}, this); continue; } else if (isScreen) { // 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); } } } } if (removeUserFromSessionList) { sessionManager.removeClient(client.getStreamid(), null); } } catch (Exception err) { log.error("[roomLeaveByScope]", err); } } /** * 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) */ @Override public void streamPublishStart(IBroadcastStream stream) { try { log.debug("----------- streamPublishStart"); IConnection current = Red5.getConnectionLocal(); 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 //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); // In case its a screen sharing we start a new Video for that if (currentClient.isScreenClient()) { currentClient.setScreenPublishStarted(true); sessionManager.updateClientByStreamId(streamid, currentClient, false, null); } if (!Strings.isEmpty(currentClient.getSecurityCode())) { currentClient.setBroadCastID(Long.parseLong(stream.getPublishedName())); currentClient.setIsBroadcasting(true); currentClient.setVWidth(320); currentClient.setVHeight(240); sessionManager.updateClientByStreamId(streamid, currentClient, false, null); } log.debug("newStream SEND: " + currentClient); // Notify all users of the same Scope // We need to iterate through the streams to catch if anybody is recording new MessageSender(current, "newStream", clientObjectSendToSync) { @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; } if (rcl.getIsRecording()) { log.debug("RCL getIsRecording newStream SEND"); recordingService.addRecordingByStreamId(current, streamid, currentClient, rcl.getRecordingId()); } if (rcl.isScreenClient()) { log.debug("RCL getIsScreenClient newStream SEND"); return true; } if (rcl.getPublicSID().equals(currentClient.getPublicSID())) { log.debug("RCL publicSID is equal newStream SEND"); return true; } log.debug( "RCL SEND is equal newStream SEND " + rcl.getPublicSID() + " || " + rcl.getUserport()); return false; } }.start(); } catch (Exception err) { log.error("[streamPublishStart]", err); } } public IBroadcastScope getBroadcastScope(IScope scope, String name) { IBasicScope basicScope = scope.getBasicScope(ScopeType.BROADCAST, name); if (!(basicScope instanceof IBroadcastScope)) { return null; } else { return (IBroadcastScope) basicScope; } } /** * 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) */ @Override public void streamBroadcastClose(IBroadcastStream stream) { // Notify all the clients that the stream had been closed log.debug("start streamBroadcastClose broadcast close: " + stream.getPublishedName()); try { IConnection current = Red5.getConnectionLocal(); Client rcl = sessionManager.getClientByStreamId(current.getClient().getId(), null); sendClientBroadcastNotifications(stream, "closeStream", rcl); } catch (Exception e) { log.error("[streamBroadcastClose]", e); } } /** * This method handles the notification room-based * * @return void * */ private void sendClientBroadcastNotifications(IBroadcastStream stream, String clientFunction, Client rc) { try { // Store the local so that we do not send notification to ourself back IConnection current = Red5.getConnectionLocal(); String streamid = current.getClient().getId(); Client currentClient = sessionManager.getClientByStreamId(streamid, null); if (currentClient == null) { // In case the client has already left(kicked) this message // might be thrown later then the RoomLeave // event and the currentClient is already gone // The second Use-Case where the currentClient is maybe null is // if we remove the client because its a Zombie/Ghost return; } // Notify all the clients that the stream had been started log.debug("sendClientBroadcastNotifications: " + stream.getPublishedName()); log.debug("sendClientBroadcastNotifications : " + currentClient + " " + currentClient.getStreamid()); // Notify all clients of the same scope (room) for (IConnection conn : current.getScope().getClientConnections()) { if (conn != null) { if (conn instanceof IServiceCapableConnection) { if (conn.equals(current)) { // there is a Bug in the current implementation // of the appDisconnect if (clientFunction.equals("closeStream")) { Client rcl = sessionManager.getClientByStreamId(conn.getClient().getId(), null); if (clientFunction.equals("closeStream") && rcl.getIsRecording()) { log.debug("*** stopRecordingShowForClient Any Client is Recording - stop that"); // StreamService.stopRecordingShowForClient(conn, // currentClient, // rcl.getRoomRecordingName(), false); recordingService.stopRecordingShowForClient(conn, currentClient); } // Don't notify current client current.ping(); } continue; } else { Client rcl = sessionManager.getClientByStreamId(conn.getClient().getId(), null); if (rcl != null) { if (rcl.isScreenClient()) { // continue; } else { log.debug("is this users still alive? :" + rcl); IServiceCapableConnection iStream = (IServiceCapableConnection) conn; iStream.invoke(clientFunction, new Object[] { rc }, this); } log.debug("sending notification to " + conn + " ID: "); // if this close stream event then stop the // recording of this stream if (clientFunction.equals("closeStream") && rcl.getIsRecording()) { log.debug( "*** +++++++ ######## sendClientBroadcastNotifications Any Client is Recording - stop that"); recordingService.stopRecordingShowForClient(conn, currentClient); } } } } } } } catch (Exception err) { log.error("[sendClientBroadcastNotifications]", err); } } /** * Adds a Moderator by its publicSID * * @param publicSID * @return -1 */ public long addModerator(String publicSID) { try { log.debug("----------- addModerator: " + publicSID); Client currentClient = sessionManager.getClientByPublicSID(publicSID, null); if (currentClient == null) { return -1L; } Long roomId = currentClient.getRoomId(); currentClient.setIsMod(true); // Put the mod-flag to true for this client sessionManager.updateClientByStreamId(currentClient.getStreamid(), currentClient, false, null); List<Client> currentMods = sessionManager.getCurrentModeratorByRoom(roomId); //Send message to all users sendMessageToCurrentScope("setNewModeratorByList", currentMods, true); } catch (Exception err) { log.error("[addModerator]", err); } return -1L; } @SuppressWarnings("unchecked") public void setNewCursorPosition(Object item) { try { IConnection current = Red5.getConnectionLocal(); Client c = sessionManager.getClientByStreamId(current.getClient().getId(), null); @SuppressWarnings("rawtypes") Map cursor = (Map) item; cursor.put("streamPublishName", c.getStreamPublishName()); sendMessageToCurrentScope("newRed5ScreenCursor", cursor, true, false); } catch (Exception err) { log.error("[setNewCursorPosition]", err); } } public long removeModerator(String publicSID) { try { log.debug("----------- removeModerator: " + publicSID); Client currentClient = sessionManager.getClientByPublicSID(publicSID, null); if (currentClient == null) { return -1L; } Long roomId = currentClient.getRoomId(); currentClient.setIsMod(false); // Put the mod-flag to true for this client sessionManager.updateClientByStreamId(currentClient.getStreamid(), currentClient, false, null); List<Client> currentMods = sessionManager.getCurrentModeratorByRoom(roomId); sendMessageToCurrentScope("setNewModeratorByList", currentMods, true); } catch (Exception err) { log.error("[removeModerator]", err); } return -1L; } public long setBroadCastingFlag(String publicSID, boolean value, boolean canVideo, Integer interviewPodId) { try { log.debug("----------- setBroadCastingFlag: " + publicSID); Client currentClient = sessionManager.getClientByPublicSID(publicSID, null); if (currentClient == null) { return -1L; } currentClient.setIsBroadcasting(value); currentClient.setCanVideo(value && canVideo); //set to false in case NOT broadcasting currentClient.setInterviewPodId(interviewPodId); // Put the mod-flag to true for this client sessionManager.updateClientByStreamId(currentClient.getStreamid(), currentClient, false, null); // Notify all clients of the same scope (room) sendMessageToCurrentScope("setNewBroadCastingFlag", currentClient, true); } catch (Exception err) { log.error("[setBroadCastingFlag]", err); } return -1L; } public long giveExclusiveAudio(String publicSID) { try { log.debug("----------- giveExclusiveAudio: " + publicSID); IConnection current = Red5.getConnectionLocal(); // String streamid = current.getClient().getId(); final Client currentClient = sessionManager.getClientByPublicSID(publicSID, null); if (currentClient == null) { return -1L; } // Put the mod-flag to true for this client currentClient.setMicMuted(false); sessionManager.updateClientByStreamId(currentClient.getStreamid(), currentClient, false, null); // Notify all clients of the same scope (room) new MessageSender(current, "receiveExclusiveAudioFlag", currentClient) { @Override public boolean filter(IConnection conn) { Client rcl = sessionManager.getClientByStreamId(conn.getClient().getId(), null); if (rcl == null) { } else if (rcl.isScreenClient()) { } else { if (rcl != currentClient) { rcl.setMicMuted(true); sessionManager.updateClientByStreamId(rcl.getStreamid(), rcl, false, null); } return false; } return true; } }.start(); } catch (Exception err) { log.error("[giveExclusiveAudio]", err); } return -1L; } public long switchMicMuted(String publicSID, boolean mute) { try { log.debug("----------- switchMicMuted: " + publicSID); Client currentClient = sessionManager.getClientByPublicSID(publicSID, null); if (currentClient == null) { return -1L; } currentClient.setMicMuted(mute); sessionManager.updateClientByStreamId(currentClient.getStreamid(), currentClient, false, null); HashMap<Integer, Object> newMessage = new HashMap<Integer, Object>(); newMessage.put(0, "updateMuteStatus"); newMessage.put(1, currentClient); sendMessageWithClient(newMessage); } catch (Exception err) { log.error("[switchMicMuted]", err); } return 0L; } public boolean getMicMutedByPublicSID(String publicSID) { try { Client currentClient = sessionManager.getClientByPublicSID(publicSID, null); if (currentClient == null) { return true; } //Put the mod-flag to true for this client return currentClient.getMicMuted(); } catch (Exception err) { log.error("[getMicMutedByPublicSID]", err); } return true; } /** * Invoked by a User whenever he want to become moderator this is needed, * 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 */ public long applyForModeration(String publicSID) { try { Client currentClient = sessionManager.getClientByPublicSID(publicSID, null); if (currentClient == null) { log.warn("Unable to find client by publicSID: {}", publicSID); return -1L; } List<Client> currentModList = sessionManager.getCurrentModeratorByRoom(currentClient.getRoomId()); if (currentModList.size() > 0) { return 2L; } else { // No moderator in this room at the moment Room room = roomDao.get(currentClient.getRoomId()); return room.isModerated() ? 3L : 1L; } } catch (Exception err) { log.error("[applyForModeration]", err); } return -1L; } /** * there will be set an attribute called "broadCastCounter" this is the name * this user will publish his stream * * @return long broadCastId */ public long getBroadCastId() { try { log.debug("----------- getBroadCastId"); IConnection current = Red5.getConnectionLocal(); String streamid = current.getClient().getId(); Client client = sessionManager.getClientByStreamId(streamid, null); client.setBroadCastID(broadCastCounter.getAndIncrement()); sessionManager.updateClientByStreamId(streamid, client, false, null); return client.getBroadCastID(); } catch (Exception err) { log.error("[getBroadCastId]", err); } return -1; } /** * this must be set _after_ the Video/Audio-Settings have been chosen (see * editrecordstream.lzx) but _before_ anything else happens, it cannot be * applied _after_ the stream has started! avsettings can be: av - video and * audio a - audio only v - video only n - no a/v only static image * furthermore * * @param avsettings * @param newMessage * @param vWidth * @param vHeight * @param roomId * @param publicSID * @param interviewPodId * @return RoomClient being updated in case of no errors, null otherwise */ public Client setUserAVSettings(String avsettings, Object newMessage, Integer vWidth, Integer vHeight, long roomId, String publicSID, Integer interviewPodId) { try { IConnection current = Red5.getConnectionLocal(); IClient c = current.getClient(); String streamid = c.getId(); log.debug("----------- setUserAVSettings {} {} {}", new Object[] { streamid, publicSID, avsettings, newMessage }); Client parentClient = sessionManager.getClientByPublicSID(publicSID, null); Client currentClient = sessionManager.getClientByStreamId(streamid, null); if (parentClient == null || currentClient == null) { log.warn("Failed to find appropriate clients"); return null; } currentClient.setAvsettings(avsettings); currentClient.setRoomId(roomId); currentClient.setPublicSID(publicSID); currentClient.setVWidth(vWidth); currentClient.setVHeight(vHeight); currentClient.setInterviewPodId(interviewPodId); currentClient.setUserId(parentClient.getUserId()); currentClient.setLastname(parentClient.getLastname()); currentClient.setFirstname(parentClient.getFirstname()); currentClient.setPicture_uri(parentClient.getPicture_uri()); sessionManager.updateAVClientByStreamId(streamid, currentClient, null); SessionVariablesUtil.initClient(c, publicSID); HashMap<String, Object> hsm = new HashMap<String, Object>(); hsm.put("client", currentClient); hsm.put("message", newMessage); sendMessageToCurrentScope("sendVarsToMessageWithClient", hsm, true); return currentClient; } catch (Exception err) { log.error("[setUserAVSettings]", err); } return null; } /* * checks if the user is allowed to apply for Moderation */ public boolean checkRoomValues(Long roomId) { try { // appointed meeting or moderated Room? Room room = roomDao.get(roomId); // not really - default logic if (!room.isAppointment() && room.isModerated()) { // if this is a Moderated Room then the Room can be only // locked off by the Moderator Bit List<Client> clientModeratorListRoom = sessionManager.getCurrentModeratorByRoom(roomId); // If there is no Moderator yet and we are asking for it // then deny it // cause at this moment, the user should wait untill a // Moderator enters the Room return clientModeratorListRoom.size() != 0; } else { // FIXME: TODO: For Rooms that are created as Appointment we // have to check that too // but I don't know yet the Logic behind it - swagner 19.06.2009 return true; } } catch (Exception err) { log.error("[checkRoomValues]", err); } return false; } /** * 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/> * <br/> * Some rules:<br/> * <ul> * <li>If it is a room that was created through the calendar, the user that * organized the room will be moderator, the param Boolean becomeModerator * will be ignored then</li> * <li>In regular rooms you can use the param Boolean becomeModerator to set * any user to become a moderator of the room</li> * </ul> * <br/> * If a new moderator is detected a Push Call to all current users of the * room is invoked "setNewModeratorByList" to notify them of the new * moderator<br/> * <br/> * At the end of the mechanism a push call with the new client-object * and all the informations about the new user is send to every user of the * current conference room<br/> * <br/> * * @param roomId - id of the room * @param becomeModerator - is user will become moderator * @param isSuperModerator - is user super moderator * @param groupId - group id of the user * @param colorObj - some color * @return RoomStatus object */ public RoomStatus setRoomValues(Long roomId, boolean becomeModerator, boolean isSuperModerator, String colorObj) { try { log.debug("----------- setRoomValues"); IConnection current = Red5.getConnectionLocal(); String streamid = current.getClient().getId(); Client client = sessionManager.getClientByStreamId(streamid, null); client.setRoomId(roomId); client.setRoomEnter(new Date()); client.setUsercolor(colorObj); Long userId = client.getUserId(); User u = userId == null ? null : userDao.get(userId > 0 ? userId : -userId); // Inject externalUserId if nothing is set yet if (client.getExternalUserId() == null && u != null) { client.setExternalUserId(u.getExternalId()); client.setExternalUserType(u.getExternalType()); } Room r = roomDao.get(roomId); if (r.getShowMicrophoneStatus()) { client.setCanGiveAudio(true); } sessionManager.updateClientByStreamId(streamid, client, true, null); // first save to get valid room count // Log the User conferenceLogDao.add(ConferenceLog.Type.roomEnter, client.getUserId(), streamid, roomId, client.getUserip(), ""); // Check for Moderation LogicalRoom ENTER List<Client> roomClients = sessionManager.getClientListByRoom(roomId); // Return Object RoomStatus roomStatus = new RoomStatus(); // appointed meeting or moderated Room? => Check Max Users first if (roomClients.size() > r.getNumberOfPartizipants()) { roomStatus.setRoomFull(true); return roomStatus; } if (isSuperModerator) { // This can be set without checking for Moderation Flag client.setIsSuperModerator(isSuperModerator); client.setIsMod(isSuperModerator); } else { Room.Right rr = AuthLevelUtil.getRoomRight(u, r, r.isAppointment() ? appointmentDao.getByRoom(r.getId()) : null, roomClients.size()); client.setIsSuperModerator(rr == Room.Right.superModerator); client.setIsMod(becomeModerator || rr == Room.Right.moderator); } if (client.getIsMod()) { // Update the Client List 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); //Status object for Shared Browsing BrowserStatus browserStatus = (BrowserStatus) current.getScope().getAttribute("browserStatus"); if (browserStatus == null) { browserStatus = new BrowserStatus(); } // RoomStatus roomStatus = new RoomStatus(); // FIXME: Rework Client Object to DTOs roomStatus.setClientList(roomClients); roomStatus.setBrowserStatus(browserStatus); return roomStatus; } catch (Exception err) { log.error("[setRoomValues]", err); } return null; } /** * 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 * @param firstname - firstname of the user * @param lastname - lastname of the user * @return RoomClient in case of everything is OK, null otherwise */ public Client setUsernameAndSession(String SID, Long userId, String username, String firstname, String lastname) { try { log.debug("----------- setUsernameAndSession"); IConnection current = Red5.getConnectionLocal(); String streamid = current.getClient().getId(); Client currentClient = sessionManager.getClientByStreamId(streamid, null); currentClient.setUsername(username); currentClient.setUserId(userId); SessionVariablesUtil.setUserId(current.getClient(), userId); currentClient.setUserObject(userId, username, firstname, lastname); // Update Session Data log.debug("UDPATE SESSION " + SID + ", " + userId); sessiondataDao.updateUserWithoutSession(SID, userId); User user = userDao.get(userId); if (user != null) { currentClient.setExternalUserId(user.getExternalId()); currentClient.setExternalUserType(user.getExternalType()); } // only fill this value from User-Record // cause invited users have non // you cannot set the firstname,lastname from the UserRecord User us = userDao.get(userId); if (us != null && 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; } /** * used by the Screen-Sharing Servlet to trigger events * * @param roomId * @param message * @return the list of room clients */ public HashMap<String, Client> sendMessageByRoomAndDomain(Long roomId, Object message) { HashMap<String, Client> roomClientList = new HashMap<String, Client>(); try { log.debug("sendMessageByRoomAndDomain " + roomId); IScope globalScope = getContext().getGlobalScope(); IScope webAppKeyScope = globalScope.getScope(OpenmeetingsVariables.webAppRootKey); log.debug("webAppKeyScope " + webAppKeyScope); IScope scopeHibernate = webAppKeyScope.getScope(roomId.toString()); new MessageSender(scopeHibernate, "newMessageByRoomAndDomain", message) { @Override public boolean filter(IConnection conn) { IClient client = conn.getClient(); return SessionVariablesUtil.isScreenClient(client); } }.start(); } catch (Exception err) { log.error("[getClientListBYRoomAndDomain]", err); } return roomClientList; } public List<Client> getCurrentModeratorList() { try { IConnection current = Red5.getConnectionLocal(); Client currentClient = sessionManager.getClientByStreamId(current.getClient().getId(), null); Long roomId = currentClient.getRoomId(); return sessionManager.getCurrentModeratorByRoom(roomId); } catch (Exception err) { log.error("[getCurrentModerator]", err); } return null; } /** * 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 */ @SuppressWarnings({ "rawtypes", "unchecked" }) public int sendVarsByWhiteboardId(ArrayList whiteboardObjParam, Long whiteboardId) { try { Map whiteboardObj = new HashMap(); int i = 0; for (Object obj : whiteboardObjParam) { whiteboardObj.put(i++, obj); } // Check if this User is the Mod: IConnection current = Red5.getConnectionLocal(); Client currentClient = sessionManager.getClientByStreamId(current.getClient().getId(), null); if (currentClient == null) { return -1; } Long roomId = currentClient.getRoomId(); // log.debug("***** sendVars: " + whiteboardObj); // Store event in list String action = whiteboardObj.get(2).toString(); if (action.equals("deleteMindMapNodes")) { // Simulate Single Delete Events for z-Index List actionObject = (List) whiteboardObj.get(3); List<List> itemObjects = (List) actionObject.get(3); Map whiteboardTempObj = new HashMap(); whiteboardTempObj.put(2, "delete"); for (List itemObject : itemObjects) { List<Object> tempActionObject = new LinkedList<Object>(); tempActionObject.add("mindmapnode"); tempActionObject.add(itemObject.get(0)); // z-Index -8 tempActionObject.add(null); // simulate -7 tempActionObject.add(null); // simulate -6 tempActionObject.add(null); // simulate -5 tempActionObject.add(null); // simulate -4 tempActionObject.add(null); // simulate -3 tempActionObject.add(null); // simulate -2 tempActionObject.add(itemObject.get(1)); // Object-Name -1 whiteboardTempObj.put(3, tempActionObject); whiteboardManagement.addWhiteBoardObjectById(roomId, whiteboardTempObj, whiteboardId); } } else { whiteboardManagement.addWhiteBoardObjectById(roomId, whiteboardObj, whiteboardId); } Map<String, Object> sendObject = new HashMap<String, Object>(); sendObject.put("id", whiteboardId); sendObject.put("param", whiteboardObjParam); boolean showDrawStatus = getWhiteboardDrawStatus(); sendMessageToCurrentScope("sendVarsToWhiteboardById", new Object[] { showDrawStatus ? currentClient : null, sendObject }, false); } catch (Exception err) { log.error("[sendVarsByWhiteboardId]", err); } return 1; } public int sendVarsModeratorGeneral(Object vars) { log.debug("*..*sendVars: " + vars); try { IConnection current = Red5.getConnectionLocal(); Client currentClient = sessionManager.getClientByStreamId(current.getClient().getId(), null); log.debug("***** id: " + currentClient.getStreamid()); boolean ismod = currentClient.getIsMod(); if (ismod) { log.debug("CurrentScope :" + current.getScope().getName()); // Send to all Clients of the same Scope sendMessageToCurrentScope("sendVarsToModeratorGeneral", vars, false); return 1; } else { // log.debug("*..*you are not allowed to send: "+ismod); return -1; } } catch (Exception err) { log.error("[sendVarsModeratorGeneral]", err); } return -1; } public int sendMessage(Object newMessage) { sendMessageToCurrentScope("sendVarsToMessage", newMessage, false); return 1; } public int sendMessageAll(Object newMessage) { sendMessageToCurrentScope("sendVarsToMessage", newMessage, true); return 1; } /** * send status for shared browsing to all members except self * @param newMessage * @return 1 */ @SuppressWarnings({ "rawtypes" }) public synchronized int sendBrowserMessageToMembers(Object newMessage) { try { IConnection current = Red5.getConnectionLocal(); List newMessageList = (List) newMessage; String action = newMessageList.get(0).toString(); BrowserStatus browserStatus = (BrowserStatus) current.getScope().getAttribute("browserStatus"); if (browserStatus == null) { browserStatus = new BrowserStatus(); } if (action.equals("initBrowser") || action.equals("newBrowserURL")) { browserStatus.setBrowserInited(true); browserStatus.setCurrentURL(newMessageList.get(1).toString()); } else if (action.equals("closeBrowserURL")) { browserStatus.setBrowserInited(false); } current.getScope().setAttribute("browserStatus", browserStatus); sendMessageToCurrentScope("sendVarsToMessage", newMessage, false); } catch (Exception err) { log.error("[sendMessage]", err); } return 1; } /** * wrapper method * @param newMessage */ public void sendMessageToMembers(Object newMessage) { //Sync to all users of current scope sendMessageToCurrentScope("sendVarsToMessage", newMessage, false); } /** * 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> * <li>do not send to clients that are screen sharing clients</li> * <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 */ public void sendMessageToCurrentScope(String remoteMethodName, Object newMessage, boolean sendSelf) { sendMessageToCurrentScope(remoteMethodName, newMessage, sendSelf, false); } /** * Only temporary for load test, with return argument for the client to have a result * * @param remoteMethodName * @param newMessage * @param sendSelf * @return true */ @Deprecated public boolean loadTestSyncMessage(String remoteMethodName, Object newMessage, boolean sendSelf) { sendMessageToCurrentScope(remoteMethodName, newMessage, sendSelf, false); return true; } /** * 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> * <li>send to clients that are screen sharing clients based on parameter</li> * <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 * @param sendScreen send to the current client as well */ public void sendMessageToCurrentScope(final String remoteMethodName, final Object newMessage, final boolean sendSelf, final boolean sendScreen) { new MessageSender(remoteMethodName, newMessage) { @Override public boolean filter(IConnection conn) { IClient client = conn.getClient(); return (!sendScreen && SessionVariablesUtil.isScreenClient(client)) || (!sendSelf && client.getId().equals(current.getClient().getId())); } }.start(); } public abstract class MessageSender extends Thread { final IScope scope; 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 { if (scope == null) { log.debug(String.format("[MessageSender] -> 'Unable to send message to NULL scope' %s, %s", remoteMethodName, newMessage)); } else { if (log.isTraceEnabled()) { log.trace(String.format("[MessageSender] -> 'sending message' %s, %s", remoteMethodName, newMessage)); } // Send to all Clients of that Scope(Room) int count = 0; for (IConnection conn : scope.getClientConnections()) { if (conn != null && conn instanceof IServiceCapableConnection) { if (filter(conn)) { continue; } ((IServiceCapableConnection) conn).invoke(remoteMethodName, new Object[] { newMessage }, ScopeApplicationAdapter.this); count++; } } if (log.isTraceEnabled()) { log.trace(String.format("[MessageSender] -> 'sending message to %s clients, DONE' %s", count, remoteMethodName)); } } } catch (Exception err) { log.error(String.format("[MessageSender -> %s, %s]", remoteMethodName, newMessage), err); } } } /** * wrapper method * @param newMessage * @return 1 in case of success, -1 otherwise */ public int sendMessageWithClient(Object newMessage) { try { sendMessageWithClientWithSyncObject(newMessage, true); } catch (Exception err) { log.error("[sendMessageWithClient] ", err); return -1; } return 1; } /** * wrapper method * @param newMessage * @param sync * @return 1 in case of success, -1 otherwise */ public int sendMessageWithClientWithSyncObject(Object newMessage, boolean sync) { try { IConnection current = Red5.getConnectionLocal(); Client currentClient = sessionManager.getClientByStreamId(current.getClient().getId(), null); 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); } catch (Exception err) { log.error("[sendMessageWithClient] ", err); return -1; } return 1; } /** * 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 */ public int sendMessageById(Object newMessage, String clientId, IScope scope) { try { log.debug("### sendMessageById ###" + clientId); HashMap<String, Object> hsm = new HashMap<String, Object>(); hsm.put("message", newMessage); // broadcast Message to specific user with id inside the same Scope for (IConnection conn : scope.getClientConnections()) { if (conn != null) { if (conn instanceof IServiceCapableConnection) { if (conn.getClient().getId().equals(clientId)) { ((IServiceCapableConnection) conn).invoke("sendVarsToMessageWithClient", new Object[] { hsm }, this); } } } } } catch (Exception err) { log.error("[sendMessageWithClient] ", err); return -1; } return 1; } /** * 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 */ public int sendMessageWithClientById(Object newMessage, String clientId) { try { IConnection current = Red5.getConnectionLocal(); Client currentClient = sessionManager.getClientByStreamId(current.getClient().getId(), null); HashMap<String, Object> hsm = new HashMap<String, Object>(); hsm.put("client", currentClient); hsm.put("message", newMessage); // broadcast Message to specific user with id inside the same Scope for (IConnection conn : current.getScope().getClientConnections()) { if (conn.getClient().getId().equals(clientId)) { ((IServiceCapableConnection) conn).invoke("sendVarsToMessageWithClient", new Object[] { hsm }, this); } } } catch (Exception err) { log.error("[sendMessageWithClient] ", err); return -1; } return 1; } public void sendMessageWithClientByPublicSID(Object message, String publicSID) { try { // ApplicationContext appCtx = getContext().getApplicationContext(); IScope globalScope = getContext().getGlobalScope(); IScope webAppKeyScope = globalScope.getScope(OpenmeetingsVariables.webAppRootKey); if (publicSID == null) { log.warn("'null' publicSID was passed to sendMessageWithClientByPublicSID"); return; } // Get Room Id to send it to the correct Scope Client currentClient = sessionManager.getClientByPublicSID(publicSID, null); if (currentClient == null) { throw new Exception("Could not Find RoomClient on List publicSID: " + publicSID); } // default Scope Name String scopeName = "hibernate"; if (currentClient.getRoomId() != null) { scopeName = currentClient.getRoomId().toString(); } IScope scopeHibernate = webAppKeyScope.getScope(scopeName); // log.debug("scopeHibernate "+scopeHibernate); if (scopeHibernate != null) { // Notify the clients of the same scope (room) with userId for (IConnection conn : webAppKeyScope.getScope(scopeName).getClientConnections()) { IClient client = conn.getClient(); if (SessionVariablesUtil.isScreenClient(client)) { // screen sharing clients do not receive events continue; } if (publicSID.equals(SessionVariablesUtil.getPublicSID(client))) { ((IServiceCapableConnection) conn).invoke("newMessageByRoomAndDomain", new Object[] { message }, this); } } } else { // Scope not yet started } } catch (Exception err) { log.error("[sendMessageWithClientByPublicSID] ", err); } } /** * @deprecated this method should be reworked to use a single SQL query in * the cache to get any client in the current room that is * recording instead of iterating through connections! * @return true in case there is recording session, false otherwise, null if any exception happend */ public boolean getInterviewRecordingStatus() { try { IConnection current = Red5.getConnectionLocal(); for (IConnection conn : current.getScope().getClientConnections()) { if (conn != null) { Client rcl = sessionManager.getClientByStreamId(conn.getClient().getId(), null); if (rcl.getIsRecording()) { return true; } } } } catch (Exception err) { log.error("[getInterviewRecordingStatus]", err); } return false; } /** * @deprecated @see {@link ScopeApplicationAdapter#getInterviewRecordingStatus()} * @return - false if there were existing recording, true if recording was started successfully, null if any exception happens */ public boolean startInterviewRecording() { try { log.debug("----------- startInterviewRecording"); IConnection current = Red5.getConnectionLocal(); for (IConnection conn : current.getScope().getClientConnections()) { if (conn != null) { Client rcl = sessionManager.getClientByStreamId(conn.getClient().getId(), null); if (rcl != null && rcl.getIsRecording()) { return false; } } } Client current_rcl = sessionManager.getClientByStreamId(current.getClient().getId(), null); // Also set the Recording Flag to Record all Participants that enter // later current_rcl.setIsRecording(true); sessionManager.updateClientByStreamId(current.getClient().getId(), current_rcl, false, null); Map<String, String> interviewStatus = new HashMap<String, String>(); interviewStatus.put("action", "start"); for (IConnection conn : current.getScope().getClientConnections()) { if (conn != null) { IClient client = conn.getClient(); if (SessionVariablesUtil.isScreenClient(client)) { // screen sharing clients do not receive events continue; } ((IServiceCapableConnection) conn).invoke("interviewStatus", new Object[] { interviewStatus }, this); log.debug("-- startInterviewRecording " + interviewStatus); } } String recordingName = "Interview " + CalendarPatterns.getDateWithTimeByMiliSeconds(new Date()); recordingService.recordMeetingStream(current, current_rcl, recordingName, "", true); return true; } catch (Exception err) { log.debug("[startInterviewRecording]", err); } return false; } @SuppressWarnings({ "rawtypes" }) public boolean sendRemoteCursorEvent(final String streamid, Map messageObj) { new MessageSender("sendRemoteCursorEvent", messageObj) { @Override public boolean filter(IConnection conn) { IClient client = conn.getClient(); return !SessionVariablesUtil.isScreenClient(client) || !conn.getClient().getId().equals(streamid); } }.start(); return true; } private Long checkRecordingClient(IConnection conn) { Long recordingId = null; if (conn != null) { Client rcl = sessionManager.getClientByStreamId(conn.getClient().getId(), null); if (rcl != null && rcl.getIsRecording()) { rcl.setIsRecording(false); recordingId = rcl.getRecordingId(); rcl.setRecordingId(null); // Reset the Recording Flag to Record all // Participants that enter later sessionManager.updateClientByStreamId(conn.getClient().getId(), rcl, false, null); } } 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() { IConnection current = Red5.getConnectionLocal(); 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) { try { log.debug("----------- stopInterviewRecording"); Long clientRecordingId = currentClient.getRecordingId(); for (IConnection conn : currentScope.getClientConnections()) { Long recordingId = checkRecordingClient(conn); if (recordingId != null) { clientRecordingId = recordingId; } } if (clientRecordingId == null) { log.debug("stopInterviewRecording:: unable to find recording client"); return false; } recordingService.stopRecordAndSave(scope, currentClient, clientRecordingId); Map<String, String> interviewStatus = new HashMap<String, String>(); interviewStatus.put("action", "stop"); sendMessageToCurrentScope("interviewStatus", interviewStatus, true); sendMessageToCurrentScope("stopRecordingMessage", currentClient, true); return true; } catch (Exception err) { log.debug("[stopInterviewRecording]", err); } return false; } /** * 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() { try { IConnection current = Red5.getConnectionLocal(); Client currentClient = sessionManager.getClientByStreamId(current.getClient().getId(), null); return sessionManager.getClientListByRoom(currentClient.getRoomId()); } catch (Exception err) { log.debug("[getClientListScope]", err); } return new ArrayList<Client>(); } private boolean getWhiteboardDrawStatus() { return configurationDao.getWhiteboardDrawStatus(); } public String getCryptKey() { return configurationDao.getCryptKey(); } public IScope getRoomScope(String room) { try { IScope globalScope = getContext().getGlobalScope(); IScope webAppKeyScope = globalScope.getScope(OpenmeetingsVariables.webAppRootKey); String scopeName = "hibernate"; // If set then its a NON default Scope if (room.length() != 0) { scopeName = room; } IScope scopeHibernate = webAppKeyScope.getScope(scopeName); return scopeHibernate; } catch (Exception err) { log.error("[getRoomScope]", err); } return null; } /* * SIP transport methods */ private List<Long> getVerifiedActiveRoomIds(Server s) { List<Long> result = new ArrayList<Long>(sessionManager.getActiveRoomIdsByServer(s)); //verify for (Iterator<Long> i = result.iterator(); i.hasNext();) { Long id = i.next(); List<Client> rcs = sessionManager.getClientListByRoom(id); if (rcs.size() == 0 || (rcs.size() == 1 && rcs.get(0).isSipTransport())) { i.remove(); } } return result.isEmpty() ? result : roomDao.getSipRooms(result); } public List<Long> getActiveRoomIds() { List<Long> result = getVerifiedActiveRoomIds(null); for (Server s : serverDao.getActiveServers()) { result.addAll(getVerifiedActiveRoomIds(s)); } 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(); String streamid = current.getClient().getId(); Client client = sessionManager.getClientByStreamId(streamid, null); Long roomId = client.getRoomId(); Integer count = roomManager.getSipConferenceMembersNumber(roomId); String newNumber = getSipTransportLastname(count); log.debug("getSipConferenceMembersNumber: " + newNumber); if (!newNumber.equals(client.getLastname())) { client.setLastname(newNumber); sessionManager.updateClientByStreamId(streamid, client, false, null); log.debug("updateSipTransport: {}, {}, {}, {}, {}", new Object[] { client.getPublicSID(), client.getRoomId(), client.getFirstname(), client.getLastname(), client.getAvsettings() }); sendMessageWithClient(new String[] { "personal", client.getFirstname(), client.getLastname() }); } return count != null && count > 0 ? count - 1 : 0; } /** * Perform call to specified phone number and join to conference * * @param number * to call */ public synchronized void joinToConfCall(final String number) { IConnection current = Red5.getConnectionLocal(); String streamid = current.getClient().getId(); final Client currentClient = sessionManager.getClientByStreamId(streamid, null); try { new Thread() { @Override public void run() { sipDao.joinToConfCall(number, roomDao.get(currentClient.getRoomId())); } }.start(); } catch (Exception e) { log.error("Executing asterisk originate error: ", e); } } public synchronized String getSipNumber(Long roomId) { Room r = roomDao.get(roomId); if (r != null && r.getConfno() != null) { log.debug("getSipNumber: roomId: {}, sipNumber: {}", new Object[] { roomId, r.getConfno() }); return r.getConfno(); } return null; } public void setSipTransport(Long roomId, String publicSID, String broadCastId) { log.debug("----------- setSipTransport"); IConnection current = Red5.getConnectionLocal(); IClient c = current.getClient(); String streamid = c.getId(); // Notify all clients of the same scope (room) Client currentClient = sessionManager.getClientByStreamId(streamid, null); currentClient.setSipTransport(true); currentClient.setRoomId(roomId); currentClient.setRoomEnter(new Date()); currentClient.setFirstname("SIP Transport"); currentClient.setLastname(getSipTransportLastname(roomId)); currentClient.setBroadCastID(Long.parseLong(broadCastId)); currentClient.setIsBroadcasting(true); currentClient.setPublicSID(publicSID); currentClient.setVWidth(120); currentClient.setVHeight(90); currentClient.setPicture_uri("phone.png"); sessionManager.updateClientByStreamId(streamid, currentClient, false, null); SessionVariablesUtil.initClient(c, publicSID); sendMessageToCurrentScope("addNewUser", currentClient, false); } }