[OPENMEETINGS-551] screen-sharing is displayed in room

Project: http://git-wip-us.apache.org/repos/asf/openmeetings/repo
Commit: http://git-wip-us.apache.org/repos/asf/openmeetings/commit/e7bab5bc
Tree: http://git-wip-us.apache.org/repos/asf/openmeetings/tree/e7bab5bc
Diff: http://git-wip-us.apache.org/repos/asf/openmeetings/diff/e7bab5bc

Branch: refs/heads/master
Commit: e7bab5bc4e3cf788090135045d948566472c33c9
Parents: c79c121
Author: Maxim Solodovnik <[email protected]>
Authored: Sat Apr 15 16:49:12 2017 +0000
Committer: Maxim Solodovnik <[email protected]>
Committed: Sat Apr 15 16:49:12 2017 +0000

----------------------------------------------------------------------
 LICENSE                                         |   41 +-
 .../core/data/conference/RoomManager.java       |   13 -
 .../openmeetings/core/remote/MobileService.java |    5 +-
 .../core/remote/RecordingService.java           |    1 -
 .../core/remote/ScopeApplicationAdapter.java    | 1262 +++++++++++++
 .../openmeetings/core/remote/UserService.java   |    1 -
 .../remote/red5/ScopeApplicationAdapter.java    | 1750 ------------------
 .../org/apache/openmeetings/IApplication.java   |    1 +
 .../apache/openmeetings/screenshare/Core.java   |   99 +-
 .../openmeetings/screenshare/IScreenShare.java  |    2 +-
 .../src/main/jnlp/templates/template.jnlp       |    3 +-
 .../openmeetings/service/user/UserManager.java  |    2 +-
 .../openmeetings/util/message/RoomMessage.java  |    2 +
 .../openmeetings/web/app/Application.java       |    7 +-
 .../openmeetings/web/common/MainPanel.java      |    2 +-
 .../openmeetings/web/room/RoomBroadcaster.java  |    2 +-
 .../apache/openmeetings/web/room/RoomPanel.java |   21 +
 .../apache/openmeetings/web/room/SwfPanel.java  |   10 +-
 .../openmeetings/web/room/VideoSettings.java    |   20 +-
 .../web/room/menu/StartSharingButton.java       |   86 +-
 .../org/apache/openmeetings/web/room/room.js    |   26 +-
 .../web/room/sidebar/RoomSidebar.java           |    2 +-
 .../WEB-INF/classes/applicationContext.xml      |    2 +-
 23 files changed, 1410 insertions(+), 1950 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/openmeetings/blob/e7bab5bc/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
index a022b5a..0907aad 100644
--- a/LICENSE
+++ b/LICENSE
@@ -498,10 +498,10 @@ The GlassFish code released under the CDDL shall be 
governed by the laws of the
 
 For SLF4J:
 
-lib/jcl-over-slf4j-1.7.22.jar
-lib/jul-to-slf4j-1.7.22.jar
-lib/log4j-over-slf4j-1.7.22.jar
-lib/slf4j-api-1.7.22.jar
+lib/jcl-over-slf4j-*.jar
+lib/jul-to-slf4j-*.jar
+lib/log4j-over-slf4j-*.jar
+lib/slf4j-api-*.jar
 
 Copyright (c) 2004-2008 QOS.ch
  All rights reserved.
@@ -1468,8 +1468,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
DAMAGE.
 
 For the components:
 
-lib/logback-classic-1.1.7.jar
-lib/logback-core-1.1.7.jar
+lib/logback-classic-*.jar
+lib/logback-core-*.jar
 plugins/ecj-4.5.1.jar
 
 Eclipse Public License - v 1.0
@@ -2114,35 +2114,6 @@ As a special exception, the copyright holders of this 
library give you permissio
 
 
=======================================================================================
 
-MIT X11 License
-https://raw.githubusercontent.com/Mahoney/sysout-over-slf4j/master/LICENSE.txt
-
-For lib/sysout-over-slf4j-1.0.2.jar
-
- * Copyright (c) 2009-2012 Robert Elliot
- * All rights reserved.
- * 
- * Permission is hereby granted, free  of charge, to any person obtaining
- * a  copy  of this  software  and  associated  documentation files  (the
- * "Software"), to  deal in  the Software without  restriction, including
- * without limitation  the rights to  use, copy, modify,  merge, publish,
- * distribute,  sublicense, and/or sell  copies of  the Software,  and to
- * permit persons to whom the Software  is furnished to do so, subject to
- * the following conditions:
- * 
- * The  above  copyright  notice  and  this permission  notice  shall  be
- * included in all copies or substantial portions of the Software.
- * 
- * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
- * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
- * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- 
=======================================================================================
-
  Mozilla Public License 1.1 (MPL 1.1)
  http://www.mozilla.org/MPL/MPL-1.1.html
  

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/e7bab5bc/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/conference/RoomManager.java
----------------------------------------------------------------------
diff --git 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/conference/RoomManager.java
 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/conference/RoomManager.java
index 05c3172..05f5b16 100644
--- 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/conference/RoomManager.java
+++ 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/data/conference/RoomManager.java
@@ -32,7 +32,6 @@ import javax.persistence.criteria.Predicate;
 import javax.persistence.criteria.Root;
 
 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.dto.basic.SearchResult;
 import org.apache.openmeetings.db.entity.room.Room;
@@ -58,8 +57,6 @@ public class RoomManager {
        private ISessionManager sessionManager;
        @Autowired
        private RoomDao roomDao;
-       @Autowired
-       private SipDao sipDao;
 
        public SearchResult<Room> getRooms(int start, int max, String orderby, 
boolean asc, String search) {
                try {
@@ -294,16 +291,6 @@ public class RoomManager {
        // 
---------------------------------------------------------------------------------------------
 
        /**
-        * Returns number of SIP conference participants
-        * @param roomId id of room
-        * @return number of participants
-        */
-       public Integer getSipConferenceMembersNumber(Long roomId) {
-               Room r = roomDao.get(roomId);
-               return r == null || r.getConfno() == null ? null : 
sipDao.countUsers(r.getConfno());
-       }
-
-       /**
         * get List of RoomGroup by group and roomtype
         *
         * @param groupId

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/e7bab5bc/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
----------------------------------------------------------------------
diff --git 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
index e2976c4..be56543 100644
--- 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
+++ 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/MobileService.java
@@ -18,7 +18,7 @@
  */
 package org.apache.openmeetings.core.remote;
 
-import static 
org.apache.openmeetings.core.remote.red5.ScopeApplicationAdapter.nextBroadCastId;
+import static 
org.apache.openmeetings.core.remote.ScopeApplicationAdapter.nextBroadCastId;
 import static org.apache.openmeetings.db.util.LocaleHelper.getCountryName;
 import static 
org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DEFAULT_GROUP_ID;
 import static 
org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FRONTEND_REGISTER_KEY;
@@ -39,8 +39,7 @@ import java.util.TimeZone;
 import java.util.UUID;
 
 import org.apache.commons.lang3.time.FastDateFormat;
-import org.apache.openmeetings.core.remote.red5.ScopeApplicationAdapter;
-import 
org.apache.openmeetings.core.remote.red5.ScopeApplicationAdapter.MessageSender;
+import 
org.apache.openmeetings.core.remote.ScopeApplicationAdapter.MessageSender;
 import org.apache.openmeetings.core.remote.util.SessionVariablesUtil;
 import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.dao.basic.ChatDao;

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/e7bab5bc/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/RecordingService.java
----------------------------------------------------------------------
diff --git 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/RecordingService.java
 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/RecordingService.java
index eae5bc0..5e2fe7f 100644
--- 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/RecordingService.java
+++ 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/RecordingService.java
@@ -29,7 +29,6 @@ import org.apache.openmeetings.core.converter.BaseConverter;
 import 
org.apache.openmeetings.core.data.record.converter.InterviewConverterTask;
 import 
org.apache.openmeetings.core.data.record.converter.RecordingConverterTask;
 import org.apache.openmeetings.core.data.record.listener.StreamListener;
-import org.apache.openmeetings.core.remote.red5.ScopeApplicationAdapter;
 import org.apache.openmeetings.core.util.WebSocketHelper;
 import org.apache.openmeetings.db.dao.record.RecordingDao;
 import org.apache.openmeetings.db.dao.record.RecordingMetaDataDao;

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/e7bab5bc/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
----------------------------------------------------------------------
diff --git 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
new file mode 100644
index 0000000..52f256d
--- /dev/null
+++ 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/ScopeApplicationAdapter.java
@@ -0,0 +1,1262 @@
+/*
+ * 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;
+
+import static 
org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_SECURE;
+import static 
org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_SECURE_PROXY;
+import static 
org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_FLASH_VIDEO_CODEC;
+import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.openmeetings.IApplication;
+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.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.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.server.Sessiondata;
+import org.apache.openmeetings.db.entity.user.User;
+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.openmeetings.util.message.RoomMessage;
+import org.apache.openmeetings.util.message.TextRoomMessage;
+import org.apache.wicket.Application;
+import org.apache.wicket.util.string.StringValue;
+import org.apache.wicket.util.string.Strings;
+import org.red5.logging.Red5LoggerFactory;
+import org.red5.server.adapter.MultiThreadedApplicationAdapter;
+import org.red5.server.api.IClient;
+import org.red5.server.api.IConnection;
+import org.red5.server.api.Red5;
+import org.red5.server.api.scope.IScope;
+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;
+
+import com.github.openjson.JSONObject;
+
+public class ScopeApplicationAdapter extends MultiThreadedApplicationAdapter 
implements IPendingServiceCallback {
+       private static final Logger log = 
Red5LoggerFactory.getLogger(ScopeApplicationAdapter.class, webAppRootKey);
+       private static final String SECURITY_CODE_PARAM = "securityCode";
+       private static final String WIDTH_PARAM = "width";
+       private static final String HEIGHT_PARAM = "height";
+       public static final String FLASH_SECURE = "secure";
+       public static final String FLASH_NATIVE_SSL = "native";
+       public static final String FLASH_PORT = "rtmpPort";
+       public static final String FLASH_SSL_PORT = "rtmpsPort";
+       public static final String FLASH_VIDEO_CODEC = "videoCodec";
+       public static final String FLASH_FPS = "fps";
+       private static AtomicLong broadCastCounter = new AtomicLong(0);
+       private JSONObject flashSettings;
+
+       @Autowired
+       private ISessionManager sessionManager;
+       @Autowired
+       private RecordingService recordingService;
+       @Autowired
+       private ConfigurationDao cfgDao;
+       @Autowired
+       private SessiondataDao sessiondataDao;
+       @Autowired
+       private ConferenceLogDao conferenceLogDao;
+       @Autowired
+       private UserDao userDao;
+       @Autowired
+       private RoomDao roomDao;
+       @Autowired
+       private SipDao sipDao;
+       @Autowired
+       private RecordingDao recordingDao;
+       @Autowired
+       private ServerDao serverDao;
+
+       @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
+                       cfgDao.getCryptKey();
+
+                       // init your handler here
+                       Properties props = new Properties();
+                       try (InputStream is = new FileInputStream(new File(new 
File(OmFileHelper.getRootDir(), "conf"), "red5.properties"))) {
+                               props.load(is);
+                       }
+                       flashSettings = new JSONObject()
+                                       .put(FLASH_SECURE, 
"yes".equals(cfgDao.getConfValue(CONFIG_FLASH_SECURE, String.class, "no")))
+                                       .put(FLASH_NATIVE_SSL, 
"best".equals(cfgDao.getConfValue(CONFIG_FLASH_SECURE_PROXY, String.class, 
"none")))
+                                       .put(FLASH_PORT, 
props.getProperty("rtmp.port"))
+                                       .put(FLASH_SSL_PORT, 
props.getProperty("rtmps.port"))
+                                       .put(FLASH_VIDEO_CODEC, 
cfgDao.getConfValue(CONFIG_FLASH_VIDEO_CODEC, String.class, "h263"))
+                                       .put(FLASH_FPS, 
cfgDao.getConfValue(OpenmeetingsVariables.CONFIG_FLASH_VIDEO_FPS, 
Integer.class, "30"))
+                                       ;
+
+                       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");
+               if ("noclient".equals(uid)) {
+                       return true;
+               }
+               String securityCode = 
(String)connParams.get(SECURITY_CODE_PARAM);
+               String parentSid = (String)map.get("parentSid");
+               if (parentSid == null) {
+                       parentSid = (String)connParams.get("parentSid");
+               }
+               StringValue scn = 
StringValue.valueOf(conn.getScope().getName());
+               long roomId = scn.toLong(Long.MIN_VALUE);
+               Client rcm = new Client();
+               IApplication iapp = 
(IApplication)Application.get(OpenmeetingsVariables.wicketApplicationName);
+               if (!Strings.isEmpty(securityCode)) {
+                       //this is for external applications like ffmpeg 
[OPENMEETINGS-1574]
+                       if (roomId < 0) {
+                               log.warn("Trying to enter invalid scope using 
security code, client is rejected:: " + roomId);
+                               return rejectClient();
+                       }
+                       String _uid = null;
+                       for (org.apache.openmeetings.db.entity.basic.Client wcl 
: iapp.getOmRoomClients(roomId)) {
+                               if (wcl.getSid().equals(securityCode)) {
+                                       _uid = wcl.getUid();
+                                       break;
+                               }
+                       }
+                       if (_uid == null) {
+                               log.warn("Client is not found by security id, 
client is rejected");
+                               return rejectClient();
+                       }
+                       Client parent = 
sessionManager.getClientByPublicSID(_uid, null);
+                       if (parent == null || 
!parent.getScope().equals(scn.toString())) {
+                               log.warn("Security code is invalid, client is 
rejected");
+                               return rejectClient();
+                       }
+                       rcm.setUsername(parent.getUsername());
+                       rcm.setFirstname(parent.getFirstname());
+                       rcm.setLastname(parent.getLastname());
+                       rcm.setUserId(parent.getUserId());
+                       rcm.setPublicSID(UUID.randomUUID().toString());
+                       rcm.setSecurityCode(_uid);
+                       Number width = (Number)connParams.get(WIDTH_PARAM);
+                       Number height = (Number)connParams.get(HEIGHT_PARAM);
+                       if (width != null && height != null) {
+                               rcm.setVWidth(width.intValue());
+                               rcm.setVHeight(height.intValue());
+                       }
+               }
+               if (Strings.isEmpty(uid) && Strings.isEmpty(securityCode) && 
Strings.isEmpty(parentSid)) {
+                       log.warn("No UIDs are provided, client is rejected");
+                       return rejectClient();
+               }
+
+               if (map.containsKey("screenClient")) {
+                       org.apache.openmeetings.db.entity.basic.Client parent = 
iapp.getOmClient(uid);
+                       if (parent == null) {
+                               log.warn("Bad parent for screen-sharing client, 
client is rejected");
+                               return rejectClient();
+                       }
+                       
SessionVariablesUtil.setIsScreenClient(conn.getClient());
+                       rcm.setUserId(parent.getUserId());
+                       rcm.setScreenClient(true);
+                       rcm.setPublicSID(UUID.randomUUID().toString());
+                       rcm.setStreamPublishName(uid);
+               }
+               rcm.setStreamid(conn.getClient().getId());
+               rcm.setScope(scn.toString());
+               boolean notHibernate = !"hibernate".equals(scn.toString());
+               if (Long.MIN_VALUE != roomId) {
+                       rcm.setRoomId(roomId);
+               } 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());
+                       if (rcm.getUserId() != null) {
+                               User u = userDao.get(rcm.getUserId());
+                               if (u == null) {
+                                       log.error("Attempt of unauthorized room 
enter: USER not found, client is rejected");
+                                       return rejectClient();
+                               }
+                               rcm.setUsername(u.getLogin());
+                               rcm.setFirstname(u.getFirstname());
+                               rcm.setLastname(u.getLastname());
+                               rcm.setEmail(u.getAddress() == null ? null : 
u.getAddress().getEmail());
+                       }
+                       rcm.setSecurityCode(sd.getSessionId());
+                       rcm.setPublicSID(UUID.randomUUID().toString());
+               }
+               rcm.setUserport(conn.getRemotePort());
+               rcm.setUserip(conn.getRemoteAddress());
+               rcm.setSwfurl(swfURL);
+               rcm.setTcUrl(tcUrl);
+               if (!Strings.isEmpty(uid)) {
+                       rcm.setPublicSID(uid);
+               }
+               rcm = sessionManager.add(iapp.updateClient(rcm, false), 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
+                       User u = null;
+                       Long userId = rcm.getUserId();
+                       SessionVariablesUtil.setUserId(conn.getClient(), 
userId);
+                       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<>();
+               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);
+                                       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);
+                               }
+                               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;
+       }
+
+       /**
+        *
+        * @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<>();
+                               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("screen x,y,width,height 
{},{},{},{}", 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);
+                                               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.");
+                                       }
+                               }
+                               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;
+       }
+
+       /**
+        * 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("[roomLeave] {} {} {} {}", client.getId(), 
room.getClients().size(), room.getContextPath(), room.getName());
+
+                       Client rcl = 
sessionManager.getClientByStreamId(client.getId(), null);
+
+                       // The Room Client can be null if the Client left the 
room by using
+                       // logicalRoomLeave
+                       if (rcl != null) {
+                               log.debug("currentClient IS NOT NULL");
+                               roomLeaveByScope(rcl, room);
+                       }
+               } catch (Exception err) {
+                       log.error("[roomLeave]", err);
+               }
+       }
+
+       public void roomLeaveByScope(String uid, Long roomId) {
+               Client rcl = sessionManager.getClientByPublicSID(uid, null);
+               IScope scope = getRoomScope("" + roomId);
+               log.debug("[roomLeaveByScope] {} {} {} {}", uid, roomId, rcl, 
scope);
+               if (rcl != null && scope != null) {
+                       roomLeaveByScope(rcl, scope);
+               }
+       }
+
+       /**
+        * 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) {
+               try {
+                       log.debug("[roomLeaveByScope] currentClient " + client);
+                       if (client.isScreenClient() && 
client.isStartStreaming()) {
+                               //TODO check others/find better way
+                               WebSocketHelper.sendRoom(new 
TextRoomMessage(client.getRoomId(), client.getUserId(), 
RoomMessage.Type.sharingStoped, client.getStreamPublishName()));
+                       }
+
+                       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);
+                               }
+                       }
+                       recordingService.stopRecordingShowForClient(scope, 
client);
+
+                       // 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);
+
+                       new MessageSender(scope, "roomDisconnect", client, 
this) {
+                               @Override
+                               public boolean filter(IConnection conn) {
+                                       Client rcl = 
sessionManager.getClientByStreamId(conn.getClient().getId(), null);
+                                       if (rcl == null) {
+                                               return true;
+                                       }
+                                       boolean isScreen = rcl.isScreenClient();
+                                       if (isScreen && 
client.getPublicSID().equals(rcl.getStreamPublishName())) {
+                                               //going to terminate screen 
sharing started by this client
+                                               ((IServiceCapableConnection) 
conn).invoke("stopStream", new Object[] { }, callback);
+                                       }
+                                       return isScreen;
+                               }
+                       }.start();
+
+                       if (client.isMobile()) {
+                               IApplication app = 
(IApplication)Application.get(OpenmeetingsVariables.wicketApplicationName);
+                               app.exit(client.getPublicSID());
+                       }
+                       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 c = 
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 = c;
+
+                       // 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 (c.isScreenClient()) {
+                               c.setScreenPublishStarted(true);
+                               sessionManager.updateClientByStreamId(streamid, 
c, false, null);
+                       }
+                       if (!c.isMobile() && 
!Strings.isEmpty(c.getSecurityCode())) {
+                               
c.setBroadCastID(Long.parseLong(stream.getPublishedName()));
+                               c.setAvsettings("av");
+                               c.setIsBroadcasting(true);
+                               if (c.getVWidth() == 0 || c.getVHeight() == 0) {
+                                       c.setVWidth(320);
+                                       c.setVHeight(240);
+                               }
+                               sessionManager.updateClientByStreamId(streamid, 
c, false, null);
+                       }
+
+                       log.debug("newStream SEND: " + c);
+
+                       // 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, this) {
+                               @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 
(Strings.isEmpty(rcl.getPublicSID())) {
+                                               log.debug("publicSID IS NULL 
newStream SEND");
+                                               return true;
+                                       }
+                                       if (rcl.getIsRecording()) {
+                                               log.debug("RCL getIsRecording 
newStream SEND");
+                                               
recordingService.addRecordingByStreamId(current, c, rcl.getRecordingId());
+                                       }
+                                       if (rcl.isScreenClient()) {
+                                               log.debug("RCL 
getIsScreenClient newStream SEND");
+                                               return true;
+                                       }
+
+                                       if 
(rcl.getPublicSID().equals(c.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();
+                       JSONObject obj = new JSONObject().put("uid", 
c.getPublicSID());
+                       if (c.isScreenClient()) {
+                               obj.put("screenShare", true)
+                                               .put("uid", 
c.getStreamPublishName())
+                                               .put("broadcastId", 
stream.getPublishedName())
+                                               .put("suid", c.getPublicSID())
+                                               .put("width", c.getVWidth())
+                                               .put("height", c.getVHeight());
+                       }
+                       WebSocketHelper.sendRoom(new 
TextRoomMessage(c.getRoomId(), c.getUserId(), RoomMessage.Type.newStream, 
obj.toString()));
+               } catch (Exception err) {
+                       log.error("[streamPublishStart]", err);
+               }
+       }
+
+       /**
+        * 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();
+                       String streamId = current.getClient().getId();
+                       Client rcl = 
sessionManager.getClientByStreamId(streamId, null);
+
+                       if (rcl == 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("streamBroadcastClose : " + rcl + " " + 
rcl.getStreamid());
+                       // this close stream event, stop the recording of this 
stream
+                       if (rcl.getIsRecording()) {
+                               log.debug("***  +++++++ ######## 
sendClientBroadcastNotifications Any Client is Recording - stop that");
+                               
recordingService.stopRecordingShowForClient(current.getScope(), rcl);
+                       }
+                       if (stream.getPublishedName().equals("" + 
rcl.getBroadCastID())) {
+                               rcl.setBroadCastID(-1);
+                               rcl.setIsBroadcasting(false);
+                               rcl.setAvsettings("n");
+                       }
+                       sessionManager.updateClientByStreamId(streamId, rcl, 
false, null);
+                       // Notify all clients of the same scope (room)
+                       sendMessageToCurrentScope("closeStream", rcl, 
rcl.isMobile());
+                       if (rcl.isScreenClient()) {
+                               WebSocketHelper.sendRoom(new 
TextRoomMessage(rcl.getRoomId(), rcl.getUserId(), RoomMessage.Type.closeStream, 
rcl.getPublicSID()));
+                       }
+               } catch (Exception e) {
+                       log.error("[streamBroadcastClose]", e);
+               }
+       }
+
+       @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 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);
+
+                       Map<Integer, Object> newMessage = new HashMap<>();
+                       newMessage.put(0, "updateMuteStatus");
+                       newMessage.put(1, currentClient);
+                       sendMessageWithClient(newMessage);
+               } catch (Exception err) {
+                       log.error("[switchMicMuted]", err);
+               }
+               return 0L;
+       }
+
+       public static long nextBroadCastId() {
+               return broadCastCounter.getAndIncrement();
+       }
+
+       public int sendMessage(Object newMessage) {
+               sendMessageToCurrentScope("sendVarsToMessage", newMessage, 
false);
+               return 1;
+       }
+
+       public int sendMessageAll(Object newMessage) {
+               sendMessageToCurrentScope("sendVarsToMessage", newMessage, 
true);
+               return 1;
+       }
+
+       /**
+        * wrapper method
+        * @param newMessage
+        */
+       public void sendMessageToMembers(List<?> 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);
+       }
+
+       public void sendMessageToCurrentScope(String scopeName, String 
remoteMethodName, Object newMessage, boolean sendSelf) {
+               sendMessageToCurrentScope(scopeName, remoteMethodName, 
newMessage, sendSelf, false);
+       }
+
+       public void sendToScope(final Long roomId, String method, Object obj) {
+               new MessageSender(getRoomScope("" + roomId), method, obj, this) 
{
+                       @Override
+                       public boolean filter(IConnection conn) {
+                               Client rcl = 
sessionManager.getClientByStreamId(conn.getClient().getId(), null);
+                               return rcl == null || rcl.isScreenClient()
+                                               || rcl.getRoomId() == null || 
!rcl.getRoomId().equals(roomId) || userDao.get(rcl.getUserId()) == null;
+                       }
+               }.start();
+       }
+
+       /**
+        * 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 method The method to be called
+        * @param msg parameters
+        * @param sendSelf send to the current client as well
+        * @param sendScreen send to the current client as well
+        */
+       public void sendMessageToCurrentScope(final String method, final Object 
msg, final boolean sendSelf, final boolean sendScreen) {
+               IConnection conn = Red5.getConnectionLocal();
+               if (conn == null) {
+                       log.warn("[sendMessageToCurrentScope] -> 'Unable to 
send message using NULL connection' {}, {}", method, msg);
+                       return;
+               }
+               sendMessageToCurrentScope(conn.getScope().getName(), method, 
msg, sendSelf, sendScreen);
+       }
+
+       public void sendMessageToCurrentScope(final String scopeName, final 
String remoteMethodName, final Object newMessage, final boolean sendSelf, final 
boolean sendScreen) {
+               new MessageSender(getRoomScope(scopeName), remoteMethodName, 
newMessage, this) {
+                       @Override
+                       public boolean filter(IConnection conn) {
+                               IClient client = conn.getClient();
+                               return (!sendScreen && 
SessionVariablesUtil.isScreenClient(client))
+                                               || (!sendSelf && current != 
null && client.getId().equals(current.getClient().getId()));
+                       }
+               }.start();
+       }
+
+       public static abstract class MessageSender extends Thread {
+               final IScope scope;
+               final IConnection current;
+               final String method;
+               final Object msg;
+               final IPendingServiceCallback callback;
+
+               public MessageSender(final String remoteMethodName, final 
Object newMessage, IPendingServiceCallback callback) {
+                       this((IScope)null, remoteMethodName, newMessage, 
callback);
+               }
+
+               public MessageSender(IScope _scope, String method, Object msg, 
IPendingServiceCallback callback) {
+                       this(Red5.getConnectionLocal(), _scope, method, msg, 
callback);
+               }
+
+               public MessageSender(IConnection current, String method, Object 
msg, IPendingServiceCallback callback) {
+                       this(current, null, method, msg, callback);
+               }
+
+               public MessageSender(IConnection current, IScope _scope, String 
method, Object msg, IPendingServiceCallback callback) {
+                       this.current = current;
+                       scope = _scope == null && current != null ? 
current.getScope() : _scope;
+                       this.method = method;
+                       this.msg = msg;
+                       this.callback = callback;
+               }
+
+               public abstract boolean filter(IConnection conn);
+
+               @Override
+               public void run() {
+                       try {
+                               if (scope == null) {
+                                       log.debug("[MessageSender] -> 'Unable 
to send message to NULL scope' {}, {}", method, msg);
+                               } else {
+                                       if (log.isTraceEnabled()) {
+                                               log.trace("[MessageSender] -> 
'sending message' {}, {}", method, msg);
+                                       }
+                                       // 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(method, new Object[] { msg }, 
callback);
+                                                       count++;
+                                               }
+                                       }
+                                       if (log.isTraceEnabled()) {
+                                               log.trace("[MessageSender] -> 
'sending message to {} clients, DONE' {}", count, method);
+                                       }
+                               }
+                       } catch (Exception err) {
+                               log.error(String.format("[MessageSender -> %s, 
%s]", method, msg), 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);
+
+                       Map<String, Object> hsm = new HashMap<>();
+                       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);
+
+                       Map<String, Object> hsm = new HashMap<>();
+                       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);
+
+                       Map<String, Object> hsm = new HashMap<>();
+                       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;
+       }
+
+       /**
+        * @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
+        */
+       @Deprecated
+       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
+        */
+       @Deprecated
+       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<>();
+                       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, this) {
+
+                       @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<>();
+                       interviewStatus.put("action", "stop");
+
+                       sendMessageToCurrentScope("interviewStatus", 
interviewStatus, true);
+                       return true;
+
+               } catch (Exception err) {
+                       log.debug("[stopInterviewRecording]", err);
+               }
+               return false;
+       }
+
+       public IScope getRoomScope(String room) {
+               if (Strings.isEmpty(room)) {
+                       return null;
+               } else {
+                       IScope globalScope = getContext().getGlobalScope();
+                       IScope webAppKeyScope = 
globalScope.getScope(OpenmeetingsVariables.webAppRootKey);
+
+                       return webAppKeyScope.getScope(room);
+               }
+       }
+
+       /*
+        * SIP transport methods
+        */
+
+       private List<Long> getVerifiedActiveRoomIds(Server s) {
+               List<Long> result = new 
ArrayList<>(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);
+       }
+
+       /**
+        * Returns number of SIP conference participants
+        * @param roomId id of room
+        * @return number of participants
+        */
+       public Integer getSipConferenceMembersNumber(Long roomId) {
+               Room r = roomDao.get(roomId);
+               return r == null || r.getConfno() == null ? null : 
sipDao.countUsers(r.getConfno());
+       }
+
+       private String getSipTransportLastname(Long roomId) {
+               return 
getSipTransportLastname(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 = 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;
+       }
+
+       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);
+       }
+
+       public JSONObject getFlashSettings() {
+               return flashSettings;
+       }
+}

http://git-wip-us.apache.org/repos/asf/openmeetings/blob/e7bab5bc/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/UserService.java
----------------------------------------------------------------------
diff --git 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/UserService.java
 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/UserService.java
index 4837982..7cc741a 100644
--- 
a/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/UserService.java
+++ 
b/openmeetings-core/src/main/java/org/apache/openmeetings/core/remote/UserService.java
@@ -26,7 +26,6 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.openmeetings.IApplication;
-import org.apache.openmeetings.core.remote.red5.ScopeApplicationAdapter;
 import org.apache.openmeetings.db.dao.server.ISessionManager;
 import org.apache.openmeetings.db.dao.server.ServerDao;
 import org.apache.openmeetings.db.dao.server.SessiondataDao;

Reply via email to