com.bhbsoft.videoconference.VideoConferenceApplicationAdapter.java Source code

Java tutorial

Introduction

Here is the source code for com.bhbsoft.videoconference.VideoConferenceApplicationAdapter.java

Source

/*
 * 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 com.bhbsoft.videoconference;

import static java.lang.System.out;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

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.red5.server.util.ScopeUtils;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import com.bhbsoft.videoconference.record.FLVRecorderService;
import com.bhbsoft.videoconference.room.BrowserStatus;
import com.bhbsoft.videoconference.room.RoomStatus;
import com.bhbsoft.videoconference.security.ServerAuth;
import com.bhbsoft.videoconference.session.Client;
import com.bhbsoft.videoconference.session.SessionManager;
import com.bhbsoft.videoconference.session.util.SessionVariablesUtil;
import com.bhbsoft.videoconference.util.CalendarParser;

public class VideoConferenceApplicationAdapter extends ApplicationAdapter implements IPendingServiceCallback {
    private static final Logger log = Red5LoggerFactory.getLogger(VideoConferenceApplicationAdapter.class);

    private SessionManager sessionManager;
    private ServerAuth serverAuth;
    @Autowired
    private FLVRecorderService flvRecorderService;

    public static String lineSeperator = System.getProperty("line.separator");

    private static long broadCastCounter = 0;

    public synchronized void resultReceived(IPendingServiceCall arg0) {
        // TODO Auto-generated method stub
    }

    @Override
    public synchronized boolean appStart(IScope scope) {
        sessionManager = SessionManager.sessionStart();
        serverAuth = new ServerAuth();
        flvRecorderService = new FLVRecorderService();
        try {
            String apphome = scope.getResource("/").getFile().getAbsolutePath();

            log.debug("webAppPath : " + apphome);

            // 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

            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
        } catch (Exception err) {
            log.error("[appStart]", err);
        }
        return true;
    }

    public boolean appConnect(IConnection conn, Object[] params) {
        log.info("appConnect " + conn.getClient().getId());

        return true;
    }

    // 1 .check password - 
    // 2. then get room to connect to from server
    // 3. connect user to room
    // 4. call service to start clock
    public boolean connect(IConnection conn, IScope scope, Object[] params) {
        // This is the master connection method called every time someone connects
        // to the server.

        // Check if the user passed valid parameters.
        //params = pseudo onlineStatus role sexe  room world
        String clientRoom = "1";

        if (params == null || params.length == 0) {
            out.println("No Username Passed");
            //Reject client terminates the execution of current client
            rejectClient("Username required, client rejected.");
            return false;
        } else {
            out.println("Number of parameters passed: " + params.length + " Value: " + params[0]);
            //clientRoom =(String) params[0];

        }

        // Do password Validation;
        //boolean valid = serverAuth.validateUser("userName","Password");

        //if (!valid) {
        //   rejectClient("Username/Password failed, client rejected.");
        //}
        //Do room validation

        //ID increments each connection
        String uid = conn.getClient().getId();

        out.println("Userid: " + uid);
        // Call original method of parent class.
        if (!super.connect(conn, scope, params)) {
            return false;
        }

        roomConnect(true, scope);

        log.info("Room ScopeTxt: " + scope.getName() + clientRoom);
        IScope roomScope = ScopeUtils.resolveScope(scope, scope.getName());
        if (roomScope == null) {
            if (scope.createChildScope(scope.getName() + clientRoom)) {
                log.info("Room {} created", scope.getName() + clientRoom);
                roomScope = scope.getScope(scope.getName() + clientRoom);
            } else {
                log.info("Room {} was not created", scope.getName() + clientRoom);
            }
        } else {
            log.info("Room scope {} was found", scope.getName() + clientRoom);
        }

        roomJoin(conn.getClient(), roomScope);

        //startRecording(scope);

        return true;
    }

    public void createRoom(String room) {
        IScope roomScope = ScopeUtils.resolveScope(scope, scope.getName() + room);
        if (roomScope == null) {
            if (scope.createChildScope(scope.getName() + room)) {
                log.info("Room {} created", scope.getName() + room);
                roomScope = scope.getScope(scope.getName() + room);
            } else {
                log.info("Room {} was not created", scope.getName() + room);
            }
        } else {
            log.info("Room scope {} was found", scope.getName() + room);
        }
    }

    public boolean roomStart(IScope room) {
        log.info("Red5First.room start " + room);
        if (!super.roomStart(room))
            return false;

        return true;
    }

    public String whoami() {
        IConnection conn = Red5.getConnectionLocal();
        IClient client = conn.getClient();
        IScope scope = conn.getScope();
        return client.getId();

    }

    //@Override

    public void startRecording(IScope scope) {
        IConnection current = Red5.getConnectionLocal();
        Client currentClient = sessionManager.getClientByStreamId(current.getClient().getId());
        String streamId = current.getClient().getId();
        boolean startRecording = true;
        if (currentClient != null) {

            currentClient.setRoom_id(scope.getName());

            // Set this connection to be a RTMP-Java Client
            //   currentClient.setIsScreenClient(true);

            boolean alreadyRecording = currentClient.isStartRecording();
            if (startRecording) {
                currentClient.setStartRecording(true);
            }

            if (!alreadyRecording) {
                //returnMap.put("modus", "startRecording");

                String recordingName = "Recording " + CalendarParser.getDateWithTimeByMiliSeconds(new Date());

                // validate the deference between a meeting and a show
                //
                flvRecorderService.addRecordingByStreamId(current, scope, streamId, currentClient,
                        currentClient.getFlvRecordingId());
                //flvRecorderService.recordMeetingStream(recordingName, "", false);
            } else {
                log.warn("Recording is already started for the client id=" + currentClient.getId()
                        + ". Second request is ignored.");
            }

        } else {
            log.error("Could not find Screen Sharing Client " + current.getClient().getId());
        }
    }

    // check to see if room as been created from session scope if not then create.
    public boolean roomConnect(Boolean params, IScope scope) {
        log.info("roomConnect : connect to room!!");

        try {
            IConnection conn = Red5.getConnectionLocal();
            IServiceCapableConnection service = (IServiceCapableConnection) conn;
            String streamId = conn.getClient().getId();

            boolean isAVClient = params;

            log.info("### Client connected to Red5 Server, register Client StreamId: " + streamId + " scope "
                    + scope.getName() + " isAVClient " + isAVClient);
            log.info("params " + params);

            // Set StreamId in Client
            //   service.invoke("setId", new Object[] { streamId }, this);

            //   String swfURL = "";
            //   if (conn.getConnectParams().get("swfUrl") != null) {
            //      swfURL = conn.getConnectParams().get("swfUrl").toString();
            //   }

            Client rcm = this.sessionManager.addClientListItem(streamId, scope.getName(), conn.getRemotePort(),
                    conn.getRemoteAddress(), "swfURL", isAVClient);

            SessionVariablesUtil.initClient(conn.getClient(), isAVClient, rcm.getPublicSID());

        } catch (Exception err) {
            log.error("roomJoin", err);
        }
        return true;
    }

    /**
     * 
     * @param map
     * @return returns key,value Map with multiple return values or null in case of exception
     * 
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public synchronized Map setConnectionAsSharingClient(Map map) {
        try {
            log.debug("-----------  setConnectionAsSharingClient");
            IConnection current = Red5.getConnectionLocal();

            Client currentClient = sessionManager.getClientByStreamId(current.getClient().getId());

            if (currentClient != null) {
                boolean startRecording = Boolean.valueOf("" + map.get("startRecording"));
                boolean startStreaming = Boolean.valueOf("" + map.get("startStreaming"));
                boolean startPublishing = Boolean.valueOf("" + map.get("startPublishing"))
                        && (0 == sessionManager.getPublishingCount(currentClient.getRoom_id()));

                currentClient.setRoom_id(current.getScope().getName());

                // Set this connection to be a RTMP-Java Client
                currentClient.setIsScreenClient(true);

                SessionVariablesUtil.setIsScreenClient(current.getClient());

                currentClient.setUser_id(map.get("user_id").toString());
                SessionVariablesUtil.setUserId(current.getClient(), map.get("user_id").toString());

                boolean alreadyStreaming = currentClient.isStartStreaming();
                if (startStreaming) {
                    currentClient.setStartStreaming(true);
                }
                boolean alreadyRecording = currentClient.isStartRecording();
                if (startRecording) {
                    currentClient.setStartRecording(true);
                }
                if (startPublishing) {
                    currentClient.setStreamPublishStarted(true);
                }

                currentClient.setOrganization_id(Long.parseLong(map.get("organization_id").toString()));

                sessionManager.updateClientByStreamId(current.getClient().getId(), currentClient, false);

                Map returnMap = new HashMap();
                returnMap.put("alreadyPublished", false);

                // if is already started screen sharing, then there is no need
                // to start it again
                if (currentClient.isScreenPublishStarted()) {
                    returnMap.put("alreadyPublished", true);
                }

                currentClient.setVX(Integer.parseInt(map.get("screenX").toString()));
                currentClient.setVY(Integer.parseInt(map.get("screenY").toString()));
                currentClient.setVWidth(Integer.parseInt(map.get("screenWidth").toString()));
                currentClient.setVHeight(Integer.parseInt(map.get("screenHeight").toString()));

                log.debug("screen x,y,width,height " + currentClient.getVX() + " " + currentClient.getVY() + " "
                        + currentClient.getVWidth() + " " + currentClient.getVHeight());

                log.debug("publishName :: " + map.get("publishName"));

                currentClient.setStreamPublishName(map.get("publishName").toString());

                Client currentScreenUser = sessionManager.getClientByUserId(currentClient.getUser_id());

                currentClient.setFirstname(currentScreenUser.getFirstname());
                currentClient.setLastname(currentScreenUser.getLastname());

                // This is duplicated, but its not sure that in the meantime
                // somebody requests this Client Object Info
                sessionManager.updateClientByStreamId(current.getClient().getId(), currentClient, false);

                if (startStreaming) {
                    if (!alreadyStreaming) {
                        returnMap.put("modus", "startStreaming");

                        log.debug("start streamPublishStart Is Screen Sharing ");

                        //Send message to all users
                        syncMessageToCurrentScope("newScreenSharing", currentClient, false);
                    } else {
                        log.warn("Streaming is already started for the client id=" + currentClient.getId()
                                + ". Second request is ignored.");
                    }
                }
                //TODO: Move method
                //--break not method
                if (startRecording) {
                    if (!alreadyRecording) {
                        returnMap.put("modus", "startRecording");

                        String recordingName = "Recording "
                                + CalendarParser.getDateWithTimeByMiliSeconds(new Date());

                        // validate the deference between a meeting and a show
                        //
                        //flvRecorderService.recordMeetingStream(recordingName, "", false);
                    } else {
                        log.warn("Recording is already started for the client id=" + currentClient.getId()
                                + ". Second request is ignored.");
                    }
                }
                if (startPublishing) {
                    syncMessageToCurrentScope(
                            "startedPublishing", new Object[] { currentClient, "rtmp://" + map.get("publishingHost")
                                    + ":1935/" + map.get("publishingApp") + "/" + map.get("publishingId") },
                            false, true);
                    returnMap.put("modus", "startPublishing");
                }

                return returnMap;

            } else {
                throw new Exception("Could not find Screen Sharing Client " + current.getClient().getId());
            }

        } catch (Exception err) {
            log.error("[setConnectionAsSharingClient]", err);
        }
        return null;
    }

    public synchronized List<Integer> listRoomBroadcast() {
        HashSet<Integer> broadcastList = new HashSet<Integer>();
        IConnection current = Red5.getConnectionLocal();
        String streamid = current.getClient().getId();
        for (Set<IConnection> conset : current.getScope().getConnections()) {
            for (IConnection conn : conset) {
                if (conn != null) {
                    Client rcl = this.sessionManager.getClientByStreamId(conn.getClient().getId());
                    if (rcl == null) {
                        // continue;
                    } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                        // 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 (id != null && !broadcastList.contains(id)) {
                                broadcastList.add(id.intValue());
                            }
                        }
                    }
                }
            }
        }
        return new ArrayList<Integer>(broadcastList);
    }

    /**
     * this function is invoked directly after initial connecting
     * 
     * @return publicSID of current client
     */
    public synchronized String getPublicSID() {
        log.debug("-----------  getPublicSID");
        IConnection current = Red5.getConnectionLocal();
        Client currentClient = this.sessionManager.getClientByStreamId(current.getClient().getId());
        sessionManager.updateClientByStreamId(current.getClient().getId(), currentClient, false);
        return currentClient.getPublicSID();
    }

    /**
     * this function is invoked after a reconnect
     * 
     * @param newPublicSID
     */
    public synchronized Boolean overwritePublicSID(String newPublicSID) {
        try {
            log.debug("-----------  overwritePublicSID");
            IConnection current = Red5.getConnectionLocal();
            IClient c = current.getClient();
            Client currentClient = sessionManager.getClientByStreamId(c.getId());
            if (currentClient == null) {
                return false;
            }
            SessionVariablesUtil.initClient(c, SessionVariablesUtil.isAVClient(c), newPublicSID);
            currentClient.setPublicSID(newPublicSID);
            sessionManager.updateClientByStreamId(c.getId(), currentClient, false);
            return true;
        } catch (Exception err) {
            log.error("[overwritePublicSID]", 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(String.format("roomLeave %s %s %s %s", client.getId(), room.getClients().size(),
                    room.getContextPath(), room.getName()));

            Client currentClient = sessionManager.getClientByStreamId(client.getId());

            // 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);
        }
    }

    /**
     * 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 currentClient
     * @param currentScope
     */
    public synchronized void roomLeaveByScope(Client currentClient, IScope currentScope,
            boolean removeUserFromSessionList) {
        try {
            log.debug("currentClient " + currentClient);

            String room_id = currentClient.getRoom_id();

            log.debug("removing USername " + currentClient.getUsername() + " " + currentClient.getConnectedSince()
                    + " streamid: " + currentClient.getStreamid());

            // stop and save any recordings
            if (currentClient.getIsRecording()) {
                log.debug("*** roomLeave Current Client is Recording - stop that");
                flvRecorderService.stopRecordAndSave(currentScope, currentClient, null);
                // set to true and overwrite the default one cause otherwise no
                // notification is send
                currentClient.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 " + currentScope);

            if (currentScope != null && currentScope.getConnections() != null) {
                // Notify Users of the current Scope
                for (Set<IConnection> conset : currentScope.getConnections()) {
                    for (IConnection cons : conset) {
                        if (cons != null && cons instanceof IServiceCapableConnection) {
                            log.debug("sending roomDisconnect to {}  client id {}", cons, cons.getClient().getId());

                            Client rcl = sessionManager.getClientByStreamId(cons.getClient().getId());

                            /*
                             * 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 (!currentClient.getStreamid().equals(rcl.getStreamid())) {
                                // add Notification if another user isrecording
                                log.debug("###########[roomLeave]");
                                if (rcl.getIsRecording()) {
                                    log.debug("*** roomLeave Any Client is Recording - stop that");
                                    flvRecorderService.stopRecordingShowForClient(cons, currentClient);
                                }

                                //If the user was a avclient, we do not broadcast a message about that to everybody
                                if (currentClient.getIsAVClient()) {
                                    continue;
                                }

                                boolean isScreen = rcl.getIsScreenClient() != null && rcl.getIsScreenClient();
                                if (isScreen && currentClient.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;
                                } else if (rcl.getIsAVClient()) {
                                    // AVClients or potential AVClients do not receive events
                                    continue;
                                }

                                // Send to all connected users
                                ((IServiceCapableConnection) cons).invoke("roomDisconnect",
                                        new Object[] { currentClient }, this);
                                log.debug("sending roomDisconnect to " + cons);
                            }
                        }
                    }
                }
            }

            if (removeUserFromSessionList) {
                this.sessionManager.removeClient(currentClient.getStreamid());
            }
        } 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 synchronized void streamPublishStart(IBroadcastStream stream) {
       try {
     log.debug("-----------  streamPublishStart");
     IConnection current = Red5.getConnectionLocal();
     String streamid = current.getClient().getId();
     Client currentClient = this.sessionManager.getClientByStreamId(streamid);
        
     //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.getIsScreenClient()) {
        
        currentClient.setScreenPublishStarted(true);
        
        this.sessionManager.updateClientByStreamId(current.getClient().getId(), currentClient, false);
     }
       ****   
     //If its an audio/video client then send the session object with the full 
     //data to everybody
     // else
     if (currentClient.getIsAVClient()) {
        clientObjectSendToSync = this.sessionManager.getClientByUserId(currentClient.getUser_id());
     }
         
     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
     for (Set<IConnection> conset : current.getScope().getConnections()) {
     for (IConnection conn : conset) {
        if (conn != null) {
           if (conn instanceof IServiceCapableConnection) {
                  
              Client rcl = this.sessionManager.getClientByStreamId(conn.getClient().getId());
                  
              if (rcl == null) {
                 log.debug("RCL IS NULL newStream SEND");
                 continue;
              }
                  
              log.debug("check send to "+rcl);
                  
              if (rcl.getPublicSID() == "") {
                 log.debug("publicSID IS NULL newStream SEND");
                 continue;
              }
              if (rcl.getIsRecording()) {
                 log.debug("RCL getIsRecording newStream SEND");
                 flvRecorderService.addRecordingByStreamId(current, streamid, currentClient, rcl.getFlvRecordingId());
              }
              if (rcl.getIsAVClient()) {
                 log.debug("RCL getIsAVClient newStream SEND");
                 continue;
              }
              if (rcl.getIsScreenClient() == null || rcl.getIsScreenClient()) {
                 log.debug("RCL getIsScreenClient newStream SEND");
                 continue;
              }
                  
              if (rcl.getPublicSID().equals(currentClient.getPublicSID())) {
                 log.debug("RCL publicSID is equal newStream SEND");
                 continue;
              }
                  
              log.debug("RCL SEND is equal newStream SEND "+rcl.getPublicSID()+" || "+rcl.getUserport());
                     
              ((IServiceCapableConnection) conn).invoke("newStream", new Object[] { clientObjectSendToSync }, this);
        
           }
        }
     }
     }
       } 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 synchronized 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());
            sendClientBroadcastNotifications(stream, "closeStream", rcl);
        } catch (Exception e) {
            log.error("[streamBroadcastClose]", e);
        }
    }

    /**
     * This method handles the notification room-based
     * 
     * @return void
     * 
     */
    private synchronized 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);

            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 (Set<IConnection> conset : current.getScope().getConnections()) {
                for (IConnection conn : conset) {
                    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());
                                    if (clientFunction.equals("closeStream") && rcl.getIsRecording()) {
                                        log.debug(
                                                "*** stopRecordingShowForClient Any Client is Recording - stop that");
                                        // StreamService.stopRecordingShowForClient(conn,
                                        // currentClient,
                                        // rcl.getRoomRecordingName(), false);
                                        flvRecorderService.stopRecordingShowForClient(conn, currentClient);
                                    }
                                    // Don't notify current client
                                    current.ping();
                                }
                                continue;
                            } else {
                                Client rcl = sessionManager.getClientByStreamId(conn.getClient().getId());
                                if (rcl != null) {
                                    if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                                        // 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");
                                        flvRecorderService.stopRecordingShowForClient(conn, currentClient);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception err) {
            log.error("[sendClientBroadcastNotifications]", err);
        }
    }

    /**
     * Adds a Moderator by its publicSID
     * 
     * @param publicSID
     * @return -1
     *
    public synchronized Long addModerator(String publicSID) {
       try {
     log.debug("-----------  addModerator: " + publicSID);
        
     Client currentClient = this.sessionManager.getClientByPublicSID(publicSID, false);
        
     if (currentClient == null) {
        return -1L;
     }
     Long room_id = currentClient.getRoom_id();
        
     currentClient.setIsMod(true);
     // Put the mod-flag to true for this client
     this.sessionManager.updateClientByStreamId(currentClient.getStreamid(), currentClient, false);
        
     List<Client> currentMods = this.sessionManager
           .getCurrentModeratorByRoom(room_id);
         
     //Send message to all users
     syncMessageToCurrentScope("setNewModeratorByList", currentMods, true);
        
       } catch (Exception err) {
     log.error("[addModerator]", err);
       }
       return -1L;
    }
        
    @SuppressWarnings("unchecked")
    public void setNewCursorPosition(Object item) {
       try {
        
     IConnection current = Red5.getConnectionLocal();
     String streamid = current.getClient().getId();
     Client currentClient = this.sessionManager.getClientByStreamId(streamid);
        
     @SuppressWarnings("rawtypes")
     Map cursor = (Map) item;
     cursor.put("streamPublishName",
           currentClient.getStreamPublishName());
        
     // Notify all users of the same Scope
     for (Set<IConnection> conset : current.getScope().getConnections()) {
     for (IConnection conn : conset) {
        if (conn != null) {
           if (conn instanceof IServiceCapableConnection) {
              IClient client = conn.getClient();
              if (SessionVariablesUtil.isScreenClient(client)) {
                 // screen sharing clients do not receive events
                 continue;
              } else if (SessionVariablesUtil.isAVClient(client)) {
                 // AVClients or potential AVClients do not receive events
                 continue;
              } if (client.getId().equals(
                       current.getClient().getId())) {
                 // don't send back to same user
                 continue;
              }
              ((IServiceCapableConnection) conn).invoke("newRed5ScreenCursor", new Object[] { cursor }, this);
           }
        }
     }
     }
       } catch (Exception err) {
     log.error("[setNewCursorPosition]", err);
       }
    }
        
    public synchronized Long removeModerator(String publicSID) {
       try {
     log.debug("-----------  removeModerator: " + publicSID);
        
     IConnection current = Red5.getConnectionLocal();
     // String streamid = current.getClient().getId();
        
     Client currentClient = this.sessionManager
           .getClientByPublicSID(publicSID, false, null);
        
     if (currentClient == null) {
        return -1L;
     }
     Long room_id = currentClient.getRoom_id();
        
     currentClient.setIsMod(false);
     // Put the mod-flag to true for this client
     this.sessionManager.updateClientByStreamId(
           currentClient.getStreamid(), currentClient, false, null);
        
     List<Client> currentMods = this.sessionManager
           .getCurrentModeratorByRoom(room_id);
        
     // Notify all clients of the same scope (room)
     for (Set<IConnection> conset : current.getScope().getConnections()) {
     for (IConnection conn : conset) {
        if (conn != null) {
           if (conn instanceof IServiceCapableConnection) {
              IClient client = conn.getClient();
              if (SessionVariablesUtil.isScreenClient(client)) {
                 // screen sharing clients do not receive events
                 continue;
              } else if (SessionVariablesUtil.isAVClient(client)) {
                 // AVClients or potential AVClients do not receive events
                 continue;
              }
              ((IServiceCapableConnection) conn).invoke(
                 "setNewModeratorByList",
                    new Object[] { currentMods }, this);
           }
        }
     }
     }
       } catch (Exception err) {
     log.error("[addModerator]", err);
       }
       return -1L;
    }
    */

    public synchronized Long setBroadCastingFlag(String userId, boolean value, Integer interviewPodId) {
        try {
            log.debug("-----------  setBroadCastingFlag: " + userId);

            IConnection current = Red5.getConnectionLocal();
            // String streamid = current.getClient().getId();

            Client currentClient = sessionManager.getClientByUserId(userId);

            if (currentClient == null) {
                return -1L;
            }

            currentClient.setIsBroadcasting(value);

            // Put the mod-flag to true for this client
            sessionManager.updateClientByStreamId(currentClient.getStreamid(), currentClient, false);

            // Notify all clients of the same scope (room)
            for (Set<IConnection> conset : current.getScope().getConnections()) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        if (conn instanceof IServiceCapableConnection) {
                            IClient client = conn.getClient();
                            if (SessionVariablesUtil.isScreenClient(client)) {
                                // screen sharing clients do not receive events
                                continue;
                            } else if (SessionVariablesUtil.isAVClient(client)) {
                                // AVClients or potential AVClients do not receive events
                                continue;
                            }

                            ((IServiceCapableConnection) conn).invoke("setNewBroadCastingFlag",
                                    new Object[] { currentClient }, this);
                        }
                    }
                }
            }
        } catch (Exception err) {
            log.error("[setBroadCastingFlag]", err);
        }
        return -1L;
    }

    public synchronized Long giveExclusiveAudio(String userId) {
        try {
            log.debug("-----------  giveExclusiveAudio: " + userId);

            IConnection current = Red5.getConnectionLocal();
            // String streamid = current.getClient().getId();

            Client currentClient = this.sessionManager.getClientByUserId(userId);

            if (currentClient == null) {
                return -1L;
            }

            // Put the mod-flag to true for this client
            currentClient.setMicMuted(false);
            this.sessionManager.updateClientByStreamId(currentClient.getStreamid(), currentClient, false);

            // Notify all clients of the same scope (room)
            for (Set<IConnection> conset : current.getScope().getConnections()) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        Client rcl = this.sessionManager.getClientByStreamId(conn.getClient().getId());
                        if (rcl == null) {
                            // continue;
                        } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                            // continue;
                        } else {
                            if (rcl != currentClient) {
                                rcl.setMicMuted(true);
                                this.sessionManager.updateClientByStreamId(rcl.getStreamid(), rcl, false);
                            }
                            log.debug("Send Flag to Client: " + rcl.getUsername());
                            if (conn instanceof IServiceCapableConnection) {
                                ((IServiceCapableConnection) conn).invoke("receiveExclusiveAudioFlag",
                                        new Object[] { currentClient }, this);
                                log.debug("sending receiveExclusiveAudioFlag to " + conn);
                            }
                        }
                    }
                }
            }
        } catch (Exception err) {
            log.error("[giveExclusiveAudio]", err);
        }
        return -1L;
    }

    public synchronized Long switchMicMuted(String userId, boolean mute) {
        try {
            log.debug("-----------  switchMicMuted: " + userId);

            Client currentClient = sessionManager.getClientByUserId(userId);
            if (currentClient == null) {
                return -1L;
            }

            currentClient.setMicMuted(mute);
            sessionManager.updateClientByStreamId(currentClient.getStreamid(), currentClient, false);

            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 synchronized Boolean getMicMutedByUserId(String userId) {
        try {
            Client currentClient = this.sessionManager.getClientByUserId(userId);
            if (currentClient == null) {
                return true;
            }

            //Put the mod-flag to true for this client
            Boolean muted = currentClient.getMicMuted();
            if (null == muted) {
                muted = true;
            }

            return muted;
        } 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 synchronized Long applyForModeration(String publicSID) {
       try {
        
     Client currentClient = this.sessionManager
           .getClientByPublicSID(publicSID, false, null);
        
     List<Client> currentModList = this.sessionManager
           .getCurrentModeratorByRoom(currentClient.getRoom_id());
        
     if (currentModList.size() > 0) {
        return 2L;
     } else {
        // No moderator in this room at the moment
        Room room = roomDao.get(currentClient.getRoom_id());
        
        if (room.getIsModeratedRoom()) {
           return 3L;
        } else {
           return 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 synchronized long getBroadCastId() {
        try {
            log.debug("-----------  getBroadCastId");
            IConnection current = Red5.getConnectionLocal();
            String streamid = current.getClient().getId();
            Client currentClient = this.sessionManager.getClientByStreamId(streamid);
            currentClient.setBroadCastID(broadCastCounter++);
            this.sessionManager.updateClientByStreamId(streamid, currentClient, false);
            return currentClient.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 room_id
     * @param publicSID
     * @param interviewPodId
     * @return RoomClient being updated in case of no errors, null otherwise
     */
    public synchronized Client setUserAVSettings(String avsettings, Object newMessage, Integer vWidth,
            Integer vHeight, String room_id, String userId, Integer interviewPodId) {
        try {
            IConnection current = Red5.getConnectionLocal();
            IClient c = current.getClient();
            String streamid = c.getId();
            log.debug("-----------  setUserAVSettings {} {} {}",
                    new Object[] { streamid, userId, avsettings, newMessage });
            Client parentClient = sessionManager.getClientByUserId(userId);
            Client currentClient = sessionManager.getClientByStreamId(streamid);
            currentClient.setAvsettings(avsettings);
            currentClient.setRoom_id(room_id);
            currentClient.setVWidth(vWidth);
            currentClient.setVHeight(vHeight);
            currentClient.setUser_id(userId);
            currentClient.setLastname(parentClient.getLastname());
            currentClient.setFirstname(parentClient.getFirstname());
            currentClient.setPicture_uri(parentClient.getPicture_uri());
            sessionManager.updateAVClientByStreamId(streamid, currentClient);
            SessionVariablesUtil.initClient(c, false, userId);

            HashMap<String, Object> hsm = new HashMap<String, Object>();
            hsm.put("client", currentClient);
            hsm.put("message", newMessage);

            for (Set<IConnection> conset : current.getScope().getConnections()) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        if (conn instanceof IServiceCapableConnection) {
                            IClient client = conn.getClient();
                            if (SessionVariablesUtil.isScreenClient(client)) {
                                // screen sharing clients do not receive events
                                continue;
                            } else if (SessionVariablesUtil.isAVClient(client)) {
                                // AVClients or potential AVClients do not receive events
                                continue;
                            }
                            ((IServiceCapableConnection) conn).invoke("sendVarsToMessageWithClient",
                                    new Object[] { hsm }, this);
                        }
                    }
                }
            }
            return currentClient;
        } catch (Exception err) {
            log.error("[setUserAVSettings]", err);
        }
        return null;
    }

    /*
     * checks if the user is allowed to apply for Moderation
     *
    public synchronized Boolean checkRoomValues(Long room_id) {
       try {
        
     // appointed meeting or moderated Room?
     Room room = roomDao.get(room_id);
        
     // not really - default logic
     if (room.getAppointment() == null || room.getAppointment() == false) {
        
        if (room.getIsModeratedRoom()) {
        
           // if this is a Moderated Room then the Room can be only
           // locked off by the Moderator Bit
           List<Client> clientModeratorListRoom = this.sessionManager
                 .getCurrentModeratorByRoom(room_id);
        
           // 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
           if (clientModeratorListRoom.size() == 0) {
              return false;
           } else {
              return true;
           }
        
        } else {
           return true;
        }
        
     } 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 room_id - id of the room
     * @param becomeModerator - is user will become moderator
     * @param isSuperModerator - is user super moderator
     * @param organization_id - organization id of the user
     * @param colorObj - some color
     * @return RoomStatus object
     */
    public synchronized RoomStatus setRoomValues(String room_id, Boolean becomeModerator, Boolean isSuperModerator,
            Long organization_id, String colorObj) {
        try {
            log.debug("-----------  setRoomValues");
            // Return Object
            RoomStatus roomStatus = new RoomStatus();

            IConnection current = Red5.getConnectionLocal();
            String streamid = current.getClient().getId();
            Client currentClient = sessionManager.getClientByStreamId(streamid);
            currentClient.setRoom_id(room_id);
            currentClient.setRoomEnter(new Date());
            currentClient.setOrganization_id(organization_id);

            currentClient.setUsercolor(colorObj);

            // This can be set without checking for Moderation Flag
            currentClient.setIsSuperModerator(isSuperModerator);

            this.sessionManager.updateClientByStreamId(streamid, currentClient, true);

            // Check for Moderation LogicalRoom ENTER
            List<Client> clientListRoom = sessionManager.getClientListByRoom(room_id);

            // default logic for non regular rooms

            // If this is a normal Room Moderator rules : first come,
            // first draw ;-)
            log.debug("setRoomValues : Room" + room_id
                    + " not appointed! Moderator rules : first come, first draw ;-)");
            if (clientListRoom.size() == 1) {
                log.debug("Room is empty so set this user to be moderation role");
                currentClient.setIsMod(true);
            } else {
                log.debug("Room is already somebody so set this user not to be moderation role");

                /*
                 *    if (becomeModerator) {
                     
                 currentClient.setIsMod(true);
                    
                 // Update the Client List
                 this.sessionManager.updateClientByStreamId(streamid, currentClient, false);
                    
                     
                    
                 // There is a need to send an extra Event here,
                 // cause at this moment there could be
                 // already somebody in the Room waiting -swagner check this comment, 20.01.2012
                     
                 //Sync message to everybody
                 //syncMessageToCurrentScope("setNewModeratorByList", modRoomList, false);
                    
                   } else {
                 // The current User is not a Teacher/Admin or
                 // whatever Role that should get the Moderation
                 currentClient.setIsMod(false);
                   }
                       
                   */

                currentClient.setIsMod(false);

            }

            // Update the Client List
            this.sessionManager.updateClientByStreamId(streamid, currentClient, false);

            //Sync message to everybody
            syncMessageToCurrentScope("addNewUser", currentClient, 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(clientListRoom);
            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 synchronized Client setUsernameReconnect(String SID, String 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 = this.sessionManager.getClientByStreamId(streamid);

            currentClient.setUsername(username);
            currentClient.setUser_id(userId);
            SessionVariablesUtil.setUserId(current.getClient(), userId);
            currentClient.setPicture_uri(picture_uri);
            currentClient.setUserObject(userId, username, firstname, lastname);

            this.sessionManager.updateClientByStreamId(streamid, currentClient, false);
            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 synchronized Client setUsernameAndSession(String userId, String username, String firstname,
            String lastname) {
        try {
            log.debug("-----------  setUsernameAndSession");
            IConnection current = Red5.getConnectionLocal();
            String streamid = current.getClient().getId();
            Client currentClient = this.sessionManager.getClientByStreamId(streamid);

            currentClient.setUsername(username);
            currentClient.setUser_id(userId);
            SessionVariablesUtil.setUserId(current.getClient(), userId);
            currentClient.setUserObject(userId, username, firstname, lastname);

            // Update Session Data
            log.debug("UDPATE SESSION " + userId);

            this.sessionManager.updateClientByStreamId(streamid, currentClient, false);
            return currentClient;
        } catch (Exception err) {
            log.error("[setUsername]", err);
        }
        return null;
    }

    /**
     * used by the Screen-Sharing Servlet to trigger events
     * 
     * @param room_id
     * @param message
     * @return the list of room clients
     */
    public synchronized HashMap<String, Client> sendMessageByRoomAndDomain(Long room_id, Object message) {
        HashMap<String, Client> roomClientList = new HashMap<String, Client>();
        try {

            log.debug("sendMessageByRoomAndDomain " + room_id);

            IScope globalScope = getContext().getGlobalScope();
            IScope webAppKeyScope = globalScope.getScope("/");

            log.debug("webAppKeyScope " + webAppKeyScope);

            IScope scopeRoom = webAppKeyScope.getScope(room_id.toString());

            if (scopeRoom != null) {

                for (Set<IConnection> conset : webAppKeyScope.getScope(room_id.toString()).getConnections()) {
                    for (IConnection conn : conset) {
                        if (conn != null) {
                            if (conn instanceof IServiceCapableConnection) {
                                IClient client = conn.getClient();
                                if (SessionVariablesUtil.isScreenClient(client)) {
                                    // screen sharing clients do not receive events
                                    continue;
                                } else if (SessionVariablesUtil.isAVClient(client)) {
                                    // AVClients or potential AVClients do not receive events
                                    continue;
                                }
                                ((IServiceCapableConnection) conn).invoke("newMessageByRoomAndDomain",
                                        new Object[] { message }, this);
                            }
                        }
                    }
                }
            } else {
                log.debug("sendMessageByRoomAndDomain servlet not yet started  - roomID : '" + room_id + "'");
            }

        } catch (Exception err) {
            log.error("[getClientListBYRoomAndDomain]", err);
        }
        return roomClientList;
    }

    public synchronized List<Client> getCurrentModeratorList() {
        return null;
    }

    public synchronized int sendVarsModeratorGeneral(Object vars) {
        log.debug("*..*sendVars: " + vars);
        try {
            IConnection current = Red5.getConnectionLocal();
            Client currentClient = this.sessionManager.getClientByStreamId(current.getClient().getId());
            // Long room_id = currentClient.getRoom_id();

            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
                for (Set<IConnection> conset : current.getScope().getConnections()) {
                    for (IConnection conn : conset) {
                        if (conn != null) {
                            if (conn instanceof IServiceCapableConnection) {
                                IClient client = conn.getClient();
                                if (SessionVariablesUtil.isScreenClient(client)) {
                                    // screen sharing clients do not receive events
                                    continue;
                                } else if (SessionVariablesUtil.isAVClient(client)) {
                                    // AVClients or potential AVClients do not receive events
                                    continue;
                                }
                                if (client.getId().equals(current.getClient().getId())) {
                                    // don't send back to same user
                                    continue;
                                }
                                ((IServiceCapableConnection) conn).invoke("sendVarsToModeratorGeneral",
                                        new Object[] { vars }, this);
                            }
                        }
                    }
                }
                return 1;
            } else {
                // log.debug("*..*you are not allowed to send: "+ismod);
                return -1;
            }
        } catch (Exception err) {
            log.error("[sendVarsModeratorGeneral]", err);
        }
        return -1;
    }

    public synchronized int sendMessage(Object newMessage) {
        try {

            syncMessageToCurrentScope("sendVarsToMessage", newMessage, false);

        } catch (Exception err) {
            log.error("[sendMessage]", err);
        }
        return 1;
    }

    public synchronized int sendMessageAll(Object newMessage) {
        try {

            syncMessageToCurrentScope("sendVarsToMessage", newMessage, true);

        } catch (Exception err) {
            log.error("[sendMessage]", err);
        }
        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);

            syncMessageToCurrentScope("sendVarsToMessage", newMessage, false);

        } catch (Exception err) {
            log.error("[sendMessage]", err);
        }
        return 1;
    }

    /**
     * wrapper method
     * @param newMessage
     */
    public synchronized void sendMessageToMembers(Object newMessage) {
        //Sync to all users of current scope
        syncMessageToCurrentScope("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 synchronized void syncMessageToCurrentScope(String remoteMethodName, Object newMessage,
            boolean sendSelf) {
        syncMessageToCurrentScope(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 synchronized boolean loadTestSyncMessage(String remoteMethodName, Object newMessage, boolean sendSelf) {
        log.info(remoteMethodName, newMessage, sendSelf);
        syncMessageToCurrentScope(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 synchronized void syncMessageToCurrentScope(String remoteMethodName, Object newMessage, boolean sendSelf,
            boolean sendScreen) {
        try {
            IConnection current = Red5.getConnectionLocal();

            // Send to all Clients of that Scope(Room)
            for (Set<IConnection> conset : current.getScope().getConnections()) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        if (conn instanceof IServiceCapableConnection) {
                            IClient client = conn.getClient();
                            if (!sendScreen && SessionVariablesUtil.isScreenClient(client)) {
                                // screen sharing clients do not receive events
                                continue;
                            } else if (SessionVariablesUtil.isAVClient(client)) {
                                // AVClients or potential AVClients do not receive events
                                continue;
                            } else if (!sendSelf && client.getId().equals(current.getClient().getId())) {
                                //Do not send back to self
                                continue;
                            }
                            ((IServiceCapableConnection) conn).invoke(remoteMethodName, new Object[] { newMessage },
                                    this);

                        }
                    }
                }
            }
        } catch (Exception err) {
            log.error("[syncMessageToCurrentScope]", err);
        }
    }

    /**
     * wrapper method
     * @param newMessage
     * @return 1 in case of success, -1 otherwise
     */
    public synchronized 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 synchronized int sendMessageWithClientWithSyncObject(Object newMessage, boolean sync) {
        try {
            IConnection current = Red5.getConnectionLocal();
            Client currentClient = this.sessionManager.getClientByStreamId(current.getClient().getId());

            HashMap<String, Object> hsm = new HashMap<String, Object>();
            hsm.put("client", currentClient);
            hsm.put("message", newMessage);

            //Sync to all users of current scope
            syncMessageToCurrentScope("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 synchronized 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 (Set<IConnection> conset : scope.getConnections()) {
                for (IConnection conn : conset) {
                    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 synchronized int sendMessageWithClientById(Object newMessage, String clientId) {
        try {
            IConnection current = Red5.getConnectionLocal();
            Client currentClient = this.sessionManager.getClientByStreamId(current.getClient().getId());

            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 (Set<IConnection> conset : current.getScope().getConnections()) {
                for (IConnection conn : conset) {
                    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 synchronized void sendMessageWithClientByUserId(Object message, String userId) {
        try {
            // ApplicationContext appCtx = getContext().getApplicationContext();
            IScope globalScope = getContext().getGlobalScope();

            IScope webAppKeyScope = globalScope.getScope("/");

            // log.debug("webAppKeyScope "+webAppKeyScope);

            // Get Room Id to send it to the correct Scope
            Client currentClient = this.sessionManager.getClientByUserId(userId);

            if (currentClient == null) {
                throw new Exception("Could not Find RoomClient on List userId: " + userId);
            }
            // default Scope Name
            String scopeName = "hibernate";
            if (currentClient.getRoom_id() != null) {
                scopeName = currentClient.getRoom_id().toString();
            }

            IScope scopeHibernate = webAppKeyScope.getScope(scopeName);

            // log.debug("scopeHibernate "+scopeHibernate);

            if (scopeHibernate != null) {
                // Notify the clients of the same scope (room) with user_id

                for (Set<IConnection> conset : webAppKeyScope.getScope(scopeName).getConnections()) {
                    for (IConnection conn : conset) {
                        IClient client = conn.getClient();
                        if (SessionVariablesUtil.isScreenClient(client)) {
                            // screen sharing clients do not receive events
                            continue;
                        } else if (SessionVariablesUtil.isAVClient(client)) {
                            // AVClients or potential AVClients do not receive events
                            continue;
                        }

                        if (SessionVariablesUtil.getUserId(client).equals(userId)) {
                            ((IServiceCapableConnection) conn).invoke("newMessageByRoomAndDomain",
                                    new Object[] { message }, this);
                        }
                    }
                }
            } else {
                // Scope not yet started
            }
        } catch (Exception err) {
            log.error("[sendMessageWithClient] ", err);
        }
    }

    public synchronized void sendMessageWithClientByPublicSIDOrUser(Object message, String user_id) {
        try {
            // ApplicationContext appCtx = getContext().getApplicationContext();

            IScope globalScope = getContext().getGlobalScope();

            IScope webAppKeyScope = globalScope.getScope("/");

            // log.debug("webAppKeyScope "+webAppKeyScope);

            // Get Room Id to send it to the correct Scope
            Client currentClient = sessionManager.getClientByUserId(user_id);

            Collection<Set<IConnection>> concolset = null;

            if (currentClient == null) {
                // Must be from a previous session, search for user in current
                // scope
                IConnection current = Red5.getConnectionLocal();
                // Notify all Clients of that Scope (Room)
                concolset = current.getScope().getConnections();
            } else {
                // default Scope Name
                String scopeName = "hibernate";
                if (currentClient.getRoom_id() != null) {
                    scopeName = currentClient.getRoom_id().toString();
                }

                IScope scopeHibernate = webAppKeyScope.getScope(scopeName);

                if (scopeHibernate != null) {
                    concolset = webAppKeyScope.getScope(scopeName).getConnections();
                }
            }

            // log.debug("scopeHibernate "+scopeHibernate);

            // Notify the clients of the same scope (room) with user_id

            for (Set<IConnection> conset : concolset) {
                for (IConnection conn : conset) {
                    if (conn != null) {

                        IClient client = conn.getClient();
                        if (SessionVariablesUtil.isScreenClient(client)) {
                            // screen sharing clients do not receive events
                            continue;
                        } else if (SessionVariablesUtil.isAVClient(client)) {
                            // AVClients or potential AVClients do not receive events
                            continue;
                        }

                        if (user_id != null && SessionVariablesUtil.getUserId(client).equals(user_id)) {
                            ((IServiceCapableConnection) conn).invoke("newMessageByRoomAndDomain",
                                    new Object[] { message }, this);
                            log.debug("sendMessageWithClientByPublicSID RPC:newMessageByRoomAndDomain" + message);
                        }
                    }
                }
            }
        } catch (Exception err) {
            log.error("[sendMessageWithClient] ", err);
        }
    }

    @SuppressWarnings({ "rawtypes" })
    public synchronized Boolean sendRemoteCursorEvent(String streamid, Map messageObj) {
        try {

            IConnection current = Red5.getConnectionLocal();

            for (Set<IConnection> conset : current.getScope().getConnections()) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        IClient client = conn.getClient();
                        if (SessionVariablesUtil.isScreenClient(client)) {
                            if (conn.getClient().getId().equals(streamid)) {
                                ((IServiceCapableConnection) conn).invoke("sendRemoteCursorEvent",
                                        new Object[] { messageObj }, this);
                            }
                        }
                    }
                }
            }
        } catch (Exception err) {
            log.debug("[sendRemoteCursorEvent]", err);
        }
        return null;
    }

    private Long checkRecordingClient(IConnection conn) {
        Long flvRecordingId = null;
        if (conn != null) {
            Client rcl = sessionManager.getClientByStreamId(conn.getClient().getId());
            if (rcl != null && rcl.getIsRecording() != null && rcl.getIsRecording()) {
                rcl.setIsRecording(false);
                flvRecordingId = rcl.getFlvRecordingId();
                rcl.setFlvRecordingId(null);

                // Reset the Recording Flag to Record all
                // Participants that enter later
                sessionManager.updateClientByStreamId(conn.getClient().getId(), rcl, false);
            }
        }
        return flvRecordingId;
    }

    /**
     * Stop the recording of the streams and send event to connected users of scope
     * 
     * @return true if interview was found
     */
    public synchronized Boolean stopInterviewRecording() {
        try {
            log.debug("-----------  stopInterviewRecording");
            IConnection current = Red5.getConnectionLocal();

            Long flvRecordingId = checkRecordingClient(current);

            Collection<Set<IConnection>> concolset = current.getScope().getConnections();
            for (Set<IConnection> conset : concolset) {
                for (IConnection conn : conset) {
                    Long recordingId = checkRecordingClient(conn);
                    if (recordingId != null) {
                        flvRecordingId = recordingId;
                    }
                }
            }
            if (flvRecordingId == null) {
                log.debug("stopInterviewRecording:: unable to find recording client");
                return false;
            }

            Client currentClient = sessionManager.getClientByStreamId(current.getClient().getId());

            flvRecorderService.stopRecordAndSave(scope, currentClient, flvRecordingId);

            Map<String, String> interviewStatus = new HashMap<String, String>();
            interviewStatus.put("action", "stop");

            for (Set<IConnection> conset : concolset) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        IClient client = conn.getClient();
                        if (SessionVariablesUtil.isScreenClient(client)) {
                            // screen sharing clients do not receive events
                            continue;
                        } else if (SessionVariablesUtil.isAVClient(client)) {
                            // AVClients or potential AVClients do not receive events
                            continue;
                        }
                        ((IServiceCapableConnection) conn).invoke("interviewStatus",
                                new Object[] { interviewStatus }, this);
                    }
                }
            }
            return true;

        } catch (Exception err) {
            log.debug("[stopInterviewRecording]", err);
        }
        return null;
    }

    /**
     * Get all ClientList Objects of that room and domain Used in
     * lz.applyForModeration.lzx
     * 
     * @return all ClientList Objects of that room
     */
    public synchronized List<Client> getClientListScope() {
        try {
            IConnection current = Red5.getConnectionLocal();
            Client currentClient = this.sessionManager.getClientByStreamId(current.getClient().getId());

            return sessionManager.getClientListByRoom(currentClient.getRoom_id());

        } catch (Exception err) {
            log.debug("[getClientListScope]", err);
        }
        return new ArrayList<Client>();
    }

    public synchronized IScope getRoomScope(String room) {
        try {

            IScope globalScope = getContext().getGlobalScope();
            IScope webAppKeyScope = globalScope.getScope("/");

            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;
    }

}