org.openmeetings.app.remote.red5.ScopeApplicationAdapter.java Source code

Java tutorial

Introduction

Here is the source code for org.openmeetings.app.remote.red5.ScopeApplicationAdapter.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 org.openmeetings.app.remote.red5;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.openmeetings.app.OpenmeetingsVariables;
import org.openmeetings.app.conference.session.RoomClient;
import org.openmeetings.app.conference.whiteboard.BrowserStatus;
import org.openmeetings.app.conference.whiteboard.RoomStatus;
import org.openmeetings.app.conference.whiteboard.WhiteboardManagement;
import org.openmeetings.app.data.basic.Configurationmanagement;
import org.openmeetings.app.data.basic.Sessionmanagement;
import org.openmeetings.app.data.calendar.daos.MeetingMemberDaoImpl;
import org.openmeetings.app.data.calendar.management.AppointmentLogic;
import org.openmeetings.app.data.conference.Roommanagement;
import org.openmeetings.app.data.logs.ConferenceLogDaoImpl;
import org.openmeetings.app.data.user.Usermanagement;
import org.openmeetings.app.data.user.dao.UsersDaoImpl;
import org.openmeetings.app.persistence.beans.basic.Configuration;
import org.openmeetings.app.persistence.beans.calendar.Appointment;
import org.openmeetings.app.persistence.beans.calendar.MeetingMember;
import org.openmeetings.app.persistence.beans.rooms.Rooms;
import org.openmeetings.app.persistence.beans.user.Users;
import org.openmeetings.app.remote.FLVRecorderService;
import org.openmeetings.app.remote.WhiteBoardService;
import org.openmeetings.utils.math.CalendarPatterns;
import org.red5.client.net.rtmp.ClientExceptionHandler;
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.api.stream.IStreamListener;
import org.red5.server.api.stream.IStreamPacket;
import org.red5.server.net.rtmp.event.IRTMPEvent;
import org.red5.server.stream.StreamingProxy;
import org.red5.server.stream.message.RTMPMessage;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

public class ScopeApplicationAdapter extends ApplicationAdapter implements IPendingServiceCallback {

    private static final Logger log = Red5LoggerFactory.getLogger(ScopeApplicationAdapter.class,
            OpenmeetingsVariables.webAppRootKey);

    @Autowired
    private ClientListManager clientListManager;
    @Autowired
    private EmoticonsManager emoticonsManager;
    @Autowired
    private WhiteBoardService whiteBoardService;
    @Autowired
    private FLVRecorderService flvRecorderService;
    @Autowired
    private Configurationmanagement cfgManagement;
    @Autowired
    private AppointmentLogic appointmentLogic;
    @Autowired
    private Sessionmanagement sessionManagement;
    @Autowired
    private Usermanagement userManagement;
    @Autowired
    private Roommanagement roommanagement;
    @Autowired
    private ConferenceLogDaoImpl conferenceLogDao;
    @Autowired
    private UsersDaoImpl usersDao;
    @Autowired
    private MeetingMemberDaoImpl meetingMemberDao;

    private Map<String, StreamingProxy> streamingProxyMap = new HashMap<String, StreamingProxy>();
    // This is the Folder where all executables are written
    // for windows platform
    public static String batchFileDir = "webapps" + File.separatorChar + "ROOT" + File.separatorChar + "jod"
            + File.separatorChar;
    public static String lineSeperator = System.getProperty("line.separator");

    // The Global WebApp Path
    public static String webAppPath = "";
    public static String configDirName = "conf";
    public static String profilesPrefix = "profile_";

    public static String configKeyCryptClassName = null;
    public static Boolean whiteboardDrawStatus = null;

    private static long broadCastCounter = 0;
    public static boolean initComplete = false;

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

    @Override
    public synchronized boolean appStart(IScope scope) {
        try {
            webAppPath = scope.getResource("/").getFile().getAbsolutePath();
            batchFileDir = webAppPath + File.separatorChar + OpenmeetingsVariables.STREAMS_DIR + File.separatorChar;

            log.debug("webAppPath : " + webAppPath);
            log.debug("batchFileFir : " + batchFileDir);

            // Only load this Class one time
            // Initially this value might by empty, because the DB is empty yet
            Configuration conf = cfgManagement.getConfKey(3, "crypt_ClassName");
            if (conf != null) {
                ScopeApplicationAdapter.configKeyCryptClassName = conf.getConf_value();
            }

            // init your handler here

            // The scheduled Jobs did go into the Spring-Managed Beans, see
            // schedulerJobs.service.xml

            // Spring Definition does not work here, its too early, Instance is
            // not set yet
            emoticonsManager.loadEmot(scope);

            for (String scopeName : scope.getScopeNames()) {
                log.debug("scopeName :: " + scopeName);
            }

            ScopeApplicationAdapter.initComplete = true;

            clientListManager.removeAllClients();

            // OpenXGHttpClient.getInstance().openSIPgUserCreateTest();
            // OpenXGWrapperClient.getInstance().testConnection();
            // OpenXGClient.getInstance().testConnection();
            // ServerSocketMinaProcess serverSocketMinaProcess = new
            // ServerSocketMinaProcess();

            // serverSocketMinaProcess.doInitSocket();

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

    @Override
    public boolean roomConnect(IConnection conn, Object[] params) {
        log.debug("roomConnect : ");

        try {

            IServiceCapableConnection service = (IServiceCapableConnection) conn;
            String streamId = conn.getClient().getId();

            boolean isAVClient = false;
            if (params.length == 1) {
                isAVClient = Boolean.parseBoolean(params[0].toString());
            }

            log.debug("### Client connected to OpenMeetings, register Client StreamId: " + streamId + " scope "
                    + conn.getScope().getName() + " isAVClient " + isAVClient);
            log.debug("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();
            }

            RoomClient rcm = this.clientListManager.addClientListItem(streamId, conn.getScope().getName(),
                    conn.getRemotePort(), conn.getRemoteAddress(), swfURL, isAVClient);

            // Log the User
            conferenceLogDao.addConferenceLog("ClientConnect", rcm.getUser_id(), streamId, null, rcm.getUserip(),
                    rcm.getScope(), rcm.getExternalUserId(), rcm.getExternalUserType(), rcm.getMail(),
                    rcm.getFirstname(), rcm.getLastname());

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

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public synchronized Map screenSharerAction(Map map) {
        try {

            IConnection current = Red5.getConnectionLocal();

            RoomClient currentClient = this.clientListManager.getClientByStreamId(current.getClient().getId());

            Map returnMap = new HashMap();
            returnMap.put("result", "stopAll");

            log.debug("-----------  ");

            if (currentClient != null) {

                if (Boolean.valueOf(map.get("stopStreaming").toString())) {
                    log.debug("start streamPublishStart Is Screen Sharing -- Stop ");

                    //Send message to all users
                    syncMessageToCurrentScope("stopRed5ScreenSharing", currentClient, false);

                    if (currentClient.isStartRecording()) {
                        returnMap.put("result", "stopSharingOnly");
                    }

                    currentClient.setStartStreaming(false);
                    currentClient.setScreenPublishStarted(false);

                    clientListManager.updateClientByStreamId(currentClient.getStreamid(), currentClient);
                }
                if (Boolean.valueOf(map.get("stopRecording").toString())) {
                    if (currentClient.isStartStreaming()) {
                        returnMap.put("result", "stopRecordingOnly");
                    }

                    //Send message to all users
                    syncMessageToCurrentScope("stopRecordingMessage", currentClient, false);

                    flvRecorderService.stopRecordAndSave(current.getScope(), currentClient, null);

                    currentClient.setStartRecording(false);
                    currentClient.setIsRecording(false);

                    clientListManager.updateClientByStreamId(currentClient.getStreamid(), currentClient);
                }
                if (Boolean.valueOf(map.get("stopPublishing").toString())) {
                    streamPublishingStop();
                    if (currentClient.getIsScreenClient() && currentClient.isStartStreaming()) {
                        returnMap.put("result", "stopPublishingOnly");
                    }
                }
            }
            return returnMap;
        } catch (Exception err) {
            log.error("[screenSharerAction]", err);
        }
        return null;
    }

    public List<RoomClient> checkRed5ScreenSharing() {
        try {
            IConnection current = Red5.getConnectionLocal();
            String streamid = current.getClient().getId();

            log.debug("checkScreenSharing -2- " + streamid);

            List<RoomClient> screenSharerList = new LinkedList<RoomClient>();

            RoomClient currentClient = this.clientListManager.getClientByStreamId(streamid);

            HashMap<String, RoomClient> roomList = this.clientListManager
                    .getClientListByRoomAll(currentClient.getRoom_id());

            for (Iterator<String> iter = roomList.keySet().iterator(); iter.hasNext();) {

                RoomClient rcl = roomList.get(iter.next());

                if (rcl.isStartStreaming()) {
                    screenSharerList.add(rcl);
                }

            }

            return screenSharerList;

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

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public synchronized Map setConnectionAsSharingClient(Map map) {
        try {

            IConnection current = Red5.getConnectionLocal();
            // IServiceCapableConnection service = (IServiceCapableConnection)
            // current;

            log.debug("### setConnectionAsSharingClient: ");

            RoomClient currentClient = this.clientListManager.getClientByStreamId(current.getClient().getId());

            if (currentClient != null) {

                boolean startRecording = Boolean.valueOf(map.get("startRecording").toString());
                boolean startStreaming = Boolean.valueOf(map.get("startStreaming").toString());

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

                // Set this connection to be a RTMP-Java Client
                currentClient.setIsScreenClient(true);
                currentClient.setUser_id(Long.parseLong(map.get("user_id").toString()));

                if (startStreaming) {
                    currentClient.setStartStreaming(true);
                }

                if (startRecording) {
                    currentClient.setStartRecording(true);
                }

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

                this.clientListManager.updateClientByStreamId(current.getClient().getId(), currentClient);

                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());

                RoomClient currentScreenUser = this.clientListManager
                        .getClientByPublicSID(currentClient.getStreamPublishName(), false);

                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
                this.clientListManager.updateClientByStreamId(current.getClient().getId(), currentClient);

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

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

                    //Send message to all users
                    syncMessageToCurrentScope("newRed5ScreenSharing", currentClient, false);
                } else if (startRecording) {
                    returnMap.put("modus", "startRecording");

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

                    flvRecorderService.recordMeetingStream(recordingName, "", false);
                } else if (Boolean.valueOf(map.get("startPublishing").toString())) {
                    if (streamPublishingStart("" + map.get("publishingHost"), "" + map.get("publishingApp"),
                            "" + map.get("publishingId"))) {
                        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() {
        List<Integer> broadcastList = new ArrayList<Integer>();
        IConnection current = Red5.getConnectionLocal();
        String streamid = current.getClient().getId();
        Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
        for (Set<IConnection> conset : conCollection) {
            for (IConnection conn : conset) {
                if (conn != null) {
                    RoomClient rcl = this.clientListManager.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
                            broadcastList.add(Long.valueOf(rcl.getBroadCastID()).intValue());
                        }
                    }
                }
            }
        }
        return broadcastList;
    }

    /**
     * this function is invoked directly after initial connecting
     * 
     * @return
     */
    public synchronized String getPublicSID() {
        IConnection current = Red5.getConnectionLocal();
        RoomClient currentClient = this.clientListManager.getClientByStreamId(current.getClient().getId());
        currentClient.setIsAVClient(false);
        clientListManager.updateClientByStreamId(current.getClient().getId(), currentClient);
        return currentClient.getPublicSID();
    }

    /**
     * this function is invoked after a reconnect
     * 
     * @param newPublicSID
     */
    public synchronized Boolean overwritePublicSID(String newPublicSID) {
        try {
            IConnection current = Red5.getConnectionLocal();
            RoomClient currentClient = this.clientListManager.getClientByStreamId(current.getClient().getId());
            if (currentClient == null) {
                return false;
            }
            currentClient.setPublicSID(newPublicSID);
            this.clientListManager.updateClientByStreamId(current.getClient().getId(), currentClient);
            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("roomLeave " + client.getId() + " " + room.getClients().size() + " " + room.getContextPath()
                    + " " + room.getName());

            RoomClient currentClient = this.clientListManager.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");
                this.roomLeaveByScope(currentClient, room, true);
            }

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

    /**
     * this means a user has left a room but only logically, he didn't leave the
     * app he just left the room
     * 
     * FIXME: Is this really needed anymore if you re-connect to another scope?
     * 
     * Exit Room by Application
     * 
     */
    public synchronized void logicalRoomLeave() {
        log.debug("logicalRoomLeave ");
        try {
            IConnection current = Red5.getConnectionLocal();
            String streamid = current.getClient().getId();

            log.debug(streamid + " is leaving");

            RoomClient currentClient = this.clientListManager.getClientByStreamId(streamid);

            this.roomLeaveByScope(currentClient, current.getScope(), true);

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

    /**
     * Removes the Client from the List, stops recording, adds the Room-Leave
     * event to running recordings, clear Polls and removes Client from any list
     * 
     * This function is kind of private/protected as the client won't be able 
     * to call it with proper values.
     * 
     * @param currentClient
     * @param currentScope
     */
    public synchronized void roomLeaveByScope(RoomClient currentClient, IScope currentScope,
            boolean removeUserFromSessionList) {
        try {

            log.debug("currentClient " + currentClient);
            log.debug("currentScope " + currentScope);
            // log.debug("currentClient "+currentClient.getRoom_id());

            Long room_id = currentClient.getRoom_id();

            // Log the User
            conferenceLogDao.addConferenceLog("roomLeave", currentClient.getUser_id(), currentClient.getStreamid(),
                    room_id, currentClient.getUserip(), "", currentClient.getExternalUserId(),
                    currentClient.getExternalUserType(), currentClient.getMail(), currentClient.getFirstname(),
                    currentClient.getLastname());

            // Remove User from Sync List's
            if (room_id != null) {
                this.whiteBoardService.removeUserFromAllLists(currentScope, currentClient);
            }

            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");
                // StreamService.stopRecordAndSave(currentScope,
                // currentClient.getRoomRecordingName(), currentClient);

                this.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
                Collection<Set<IConnection>> conCollection = currentScope.getConnections();
                for (Set<IConnection> conset : conCollection) {
                    for (IConnection cons : conset) {
                        if (cons != null) {
                            if (cons instanceof IServiceCapableConnection) {

                                log.debug("sending roomDisconnect to " + cons + " client id "
                                        + cons.getClient().getId());

                                RoomClient rcl = this.clientListManager
                                        .getClientByStreamId(cons.getClient().getId());

                                /*
                                 * Check if the Client does still exist on the
                                 * list
                                 */
                                if (rcl != null) {

                                    /*
                                     * 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");
                                            this.flvRecorderService.stopRecordingShowForClient(cons, currentClient);
                                        }

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

                                        if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                                            // 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);
                                    }
                                } else {
                                    log.debug("For this StreamId: " + cons.getClient().getId()
                                            + " There is no Client in the List anymore");
                                }
                            }
                        }
                    }
                }
            }

            if (removeUserFromSessionList) {
                this.clientListManager.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
     * 
     * @return void
     * 
     */
    @Override
    public synchronized void streamPublishStart(IBroadcastStream stream) {
        try {
            IConnection current = Red5.getConnectionLocal();
            String streamid = current.getClient().getId();
            RoomClient currentClient = this.clientListManager.getClientByStreamId(streamid);

            //We make a second object the has the reference to the object 
            //that we will use to send to all participents
            RoomClient 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.clientListManager.updateClientByStreamId(current.getClient().getId(), currentClient);
            }
            //If its an audio/video client then send the session object with the full 
            //data to everybody
            else if (currentClient.getIsAVClient()) {
                clientObjectSendToSync = this.clientListManager.getClientByPublicSID(currentClient.getPublicSID(),
                        false);
            }

            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
            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        if (conn instanceof IServiceCapableConnection) {

                            RoomClient rcl = this.clientListManager.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");
                                this.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 iStream = (IServiceCapableConnection) conn;
                            iStream.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;
        }
    }

    public boolean streamPublishingStart(String host, String app, String id) {
        final boolean[] result = { true };
        final IConnection conn = Red5.getConnectionLocal();
        RoomClient rc = clientListManager.getClientByStreamId(conn.getClient().getId());
        String publishName = rc.getStreamPublishName();

        if (rc.getIsScreenClient() && rc.isStartStreaming()) {
            IScope scope = conn.getScope();
            IBroadcastStream stream = getBroadcastStream(scope, publishName);
            IBroadcastScope bsScope = getBroadcastScope(scope, publishName);
            final StreamingProxy proxy = new StreamingProxy();
            proxy.setHost(host);
            proxy.setApp(app);
            proxy.setPort(1935);
            proxy.init();
            proxy.setExceptionHandler(new ClientExceptionHandler() {
                public void handleException(Throwable throwable) {
                    result[0] = false;
                    HashMap<String, Object> params = new HashMap<String, Object>();
                    params.put("stopPublishing", true);
                    params.put("error", throwable.getMessage());
                    ((IServiceCapableConnection) conn).invoke("screenSharerAction", new Object[] { params },
                            ScopeApplicationAdapter.this);
                }
            });
            bsScope.subscribe(proxy, null);
            proxy.start(id, "live", null);
            streamingProxyMap.put(publishName, proxy);
            stream.addStreamListener(new IStreamListener() {
                public void packetReceived(IBroadcastStream stream, IStreamPacket packet) {
                    try {
                        RTMPMessage m = RTMPMessage.build((IRTMPEvent) packet, packet.getTimestamp());
                        proxy.pushMessage(null, m);
                    } catch (Exception e) {
                        log.error("Exception while sending proxy message", e);
                    }
                }
            });
        }
        return result[0];
    }

    public void streamPublishingStop() {
        IConnection current = Red5.getConnectionLocal();
        RoomClient rc = clientListManager.getClientByStreamId(current.getClient().getId());
        String publishName = rc.getStreamPublishName();

        if (rc.getIsScreenClient() && publishName != null) {
            IScope scope = current.getScope();
            IBroadcastStream stream = getBroadcastStream(scope, publishName);
            StreamingProxy proxy = streamingProxyMap.remove(publishName);
            if (proxy != null) {
                proxy.stop();
                IBroadcastScope bsScope = getBroadcastScope(scope, stream.getPublishedName());
                if (bsScope != null) {
                    bsScope.unsubscribe(proxy);
                }
            }
        }
    }

    /**
     * This method handles the Event after a stream has been removed all
     * connected Clients in the same room will get a notification
     * 
     * @return void
     * 
     */
    @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 {
            streamPublishingStop();

            IConnection current = Red5.getConnectionLocal();
            RoomClient rcl = clientListManager.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,
            RoomClient rc) {
        try {

            // Store the local so that we do not send notification to ourself
            // back
            IConnection current = Red5.getConnectionLocal();
            String streamid = current.getClient().getId();
            RoomClient currentClient = this.clientListManager.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)
            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                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")) {
                                    RoomClient rcl = this.clientListManager
                                            .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);
                                        this.flvRecorderService.stopRecordingShowForClient(conn, currentClient);
                                    }
                                    // Don't notify current client
                                    current.ping();
                                }
                                continue;
                            } else {
                                RoomClient rcl = this.clientListManager
                                        .getClientByStreamId(conn.getClient().getId());
                                if (rcl != null) {
                                    if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                                        // continue;
                                    } else {
                                        log.debug("is this users still alive? :" + rcl);
                                        // conn.ping();
                                        IServiceCapableConnection iStream = (IServiceCapableConnection) conn;
                                        // log.info("IServiceCapableConnection ID "
                                        // + iStream.getClient().getId());
                                        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");
                                        // StreamService.stopRecordingShowForClient(conn,
                                        // currentClient,
                                        // rcl.getRoomRecordingName(), false);
                                        this.flvRecorderService.stopRecordingShowForClient(conn, currentClient);
                                    }
                                }

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

    /**
     * Adds a Moderator by its publicSID
     * 
     * @param publicSID
     * @return
     */
    public synchronized Long addModerator(String publicSID) {
        try {

            log.debug("*..*addModerator publicSID: " + publicSID);

            // String streamid = current.getClient().getId();

            RoomClient currentClient = this.clientListManager.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.clientListManager.updateClientByStreamId(currentClient.getStreamid(), currentClient);

            List<RoomClient> currentMods = this.clientListManager.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();
            RoomClient currentClient = this.clientListManager.getClientByStreamId(streamid);

            // log.debug("[setNewCursorPosition]"+item);

            @SuppressWarnings("rawtypes")
            Map cursor = (Map) item;
            cursor.put("streamPublishName", currentClient.getStreamPublishName());

            // log.debug("[setNewCursorPosition x]"+cursor.get("cursor_x"));
            // log.debug("[setNewCursorPosition y]"+cursor.get("cursor_y"));
            // log.debug("[setNewCursorPosition publicSID]"+cursor.get("publicSID"));

            // Notify all users of the same Scope
            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        if (conn instanceof IServiceCapableConnection) {
                            if (conn.equals(current)) {
                                continue;
                            } else {
                                RoomClient rcl = this.clientListManager
                                        .getClientByStreamId(conn.getClient().getId());
                                if (rcl == null) {
                                    // continue;
                                } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                                    // continue;
                                } else {
                                    // log.debug("is this users still alive? :"+rcl);
                                    // Check if the Client is in the same room
                                    // and same domain
                                    IServiceCapableConnection iStream = (IServiceCapableConnection) conn;
                                    // log.info("IServiceCapableConnection ID "
                                    // + iStream.getClient().getId());
                                    iStream.invoke("newRed5ScreenCursor", new Object[] { cursor }, this);
                                    // log.debug("send Notification to");
                                }
                            }
                        }
                    }
                }
            }

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

    public synchronized Long removeModerator(String publicSID) {
        try {

            log.debug("*..*addModerator publicSID: " + publicSID);

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

            RoomClient currentClient = this.clientListManager.getClientByPublicSID(publicSID, false);

            if (currentClient == null) {
                return -1L;
            }
            Long room_id = currentClient.getRoom_id();

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

            List<RoomClient> currentMods = this.clientListManager.getCurrentModeratorByRoom(room_id);

            // Notify all clients of the same scope (room)
            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());
                        if (rcl == null) {
                            // continue;
                        } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                            // continue;
                        } else {
                            log.debug("Send Flag to Client: " + rcl.getUsername());
                            if (conn instanceof IServiceCapableConnection) {
                                ((IServiceCapableConnection) conn).invoke("setNewModeratorByList",
                                        new Object[] { currentMods }, this);
                                log.debug("sending setNewModeratorByList to " + conn);
                            }
                        }
                    }
                }
            }

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

    public synchronized Long setBroadCastingFlag(String publicSID, boolean value, Integer interviewPodId) {
        try {

            log.debug("*..*setBroadCastingFlag publicSID: " + publicSID);

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

            RoomClient currentClient = this.clientListManager.getClientByPublicSID(publicSID, false);

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

            currentClient.setIsBroadcasting(value);
            currentClient.setInterviewPodId(interviewPodId);

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

            // Notify all clients of the same scope (room)
            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());
                        if (rcl == null) {
                            continue;
                        } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                            continue;
                        } else if (rcl.getIsAVClient()) {
                            continue;
                        }

                        log.debug("Send Flag to Client: " + rcl.getUsername());
                        if (conn instanceof IServiceCapableConnection) {
                            ((IServiceCapableConnection) conn).invoke("setNewBroadCastingFlag",
                                    new Object[] { currentClient }, this);
                            log.debug("sending setNewBroadCastingFlag to " + conn);
                        }
                    }
                }
            }

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

    public synchronized Long giveExclusiveAudio(String publicSID) {
        try {

            log.debug("*..*giveExclusiveAudio publicSID: " + publicSID);

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

            RoomClient currentClient = this.clientListManager.getClientByPublicSID(publicSID, false);

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

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

            // Notify all clients of the same scope (room)
            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());
                        if (rcl == null) {
                            // continue;
                        } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                            // continue;
                        } else {
                            if (rcl != currentClient) {
                                rcl.setMicMuted(true);
                                this.clientListManager.updateClientByStreamId(rcl.getStreamid(), rcl);
                            }
                            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 publicSID, boolean mute) {
        try {
            log.debug("*..*switchMicMuted publicSID: " + publicSID);

            RoomClient currentClient = this.clientListManager.getClientByPublicSID(publicSID, false);
            if (currentClient == null) {
                return -1L;
            }

            currentClient.setMicMuted(mute);
            this.clientListManager.updateClientByStreamId(currentClient.getStreamid(), currentClient);

            HashMap<Integer, Object> newMessage = new HashMap<Integer, Object>();
            newMessage.put(0, "updateMuteStatus");
            newMessage.put(1, currentClient);
            this.sendMessageWithClient(newMessage);

        } catch (Exception err) {
            log.error("[switchMicMuted]", err);
        }
        return 0L;
    }

    public synchronized Boolean getMicMutedByPublicSID(String publicSID) {
        try {
            RoomClient currentClient = this.clientListManager.getClientByPublicSID(publicSID, false);
            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 {

            RoomClient currentClient = this.clientListManager.getClientByPublicSID(publicSID, false);

            List<RoomClient> currentModList = this.clientListManager
                    .getCurrentModeratorByRoom(currentClient.getRoom_id());

            if (currentModList.size() > 0) {
                return 2L;
            } else {
                // No moderator in this room at the moment
                Rooms room = roommanagement.getRoomById(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 {
            IConnection current = Red5.getConnectionLocal();
            String streamid = current.getClient().getId();
            RoomClient currentClient = this.clientListManager.getClientByStreamId(streamid);
            currentClient.setBroadCastID(broadCastCounter++);
            this.clientListManager.updateClientByStreamId(streamid, currentClient);
            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 avsetting
     * @param newMessage
     * @return
     */
    public synchronized RoomClient setUserAVSettings(String avsettings, Object newMessage, Integer vWidth,
            Integer vHeight, long room_id, String publicSID, Integer interviewPodId) {
        try {

            IConnection current = Red5.getConnectionLocal();
            String streamid = current.getClient().getId();
            RoomClient currentClient = this.clientListManager.getClientByStreamId(streamid);
            currentClient.setAvsettings(avsettings);
            currentClient.setRoom_id(room_id);
            currentClient.setPublicSID(publicSID);
            currentClient.setIsAVClient(true);
            currentClient.setVWidth(vWidth);
            currentClient.setVHeight(vHeight);
            currentClient.setInterviewPodId(interviewPodId);
            // Long room_id = currentClient.getRoom_id();
            this.clientListManager.updateAVClientByStreamId(streamid, currentClient);

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

            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        if (conn instanceof IServiceCapableConnection) {
                            RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());
                            if (rcl == null) {
                                // continue;
                            } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                                // continue;
                            } else {
                                ((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?
            Rooms room = roommanagement.getRoomById(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<RoomClient> clientModeratorListRoom = this.clientListManager
                            .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/>
     * And 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
     * @param colorObj
     * @return
     */
    public synchronized RoomStatus setRoomValues(Long room_id, Boolean becomeModerator, Boolean isSuperModerator,
            Long organization_id, String colorObj) {
        try {

            // Return Object
            RoomStatus roomStatus = new RoomStatus();

            IConnection current = Red5.getConnectionLocal();
            String streamid = current.getClient().getId();
            RoomClient currentClient = this.clientListManager.getClientByStreamId(streamid);
            currentClient.setRoom_id(room_id);
            currentClient.setIsAVClient(false);
            currentClient.setRoomEnter(new Date());
            currentClient.setOrganization_id(organization_id);

            currentClient.setUsercolor(colorObj);

            // Inject externalUserId if nothing is set yet
            if (currentClient.getExternalUserId() == null) {
                Users us = usersDao.getUser(currentClient.getUser_id());
                if (us != null) {
                    currentClient.setExternalUserId(us.getExternalUserId());
                    currentClient.setExternalUserType(us.getExternalUserType());
                }
            }

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

            this.clientListManager.updateClientByStreamId(streamid, currentClient);

            Rooms room = roommanagement.getRoomById(room_id);
            if (room.getShowMicrophoneStatus()) {
                currentClient.setCanGiveAudio(true);
            }

            // Log the User
            conferenceLogDao.addConferenceLog("roomEnter", currentClient.getUser_id(), streamid, room_id,
                    currentClient.getUserip(), "", currentClient.getExternalUserId(),
                    currentClient.getExternalUserType(), currentClient.getMail(), currentClient.getFirstname(),
                    currentClient.getLastname());

            log.debug("##### setRoomValues : " + currentClient);

            // Check for Moderation LogicalRoom ENTER
            HashMap<String, RoomClient> clientListRoom = this.clientListManager.getRoomClients(room_id);

            // appointed meeting or moderated Room? => Check Max Users first
            if (room.getNumberOfPartizipants() != null && clientListRoom.size() > room.getNumberOfPartizipants()) {
                roomStatus.setRoomFull(true);
                return roomStatus;
            }

            // default logic for non regular rooms
            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<RoomClient> clientModeratorListRoom =
                    // this.clientListManager.getCurrentModeratorByRoom(room_id);

                    // If there is no Moderator yet we have to check if the
                    // current User has the Bit set to true to
                    // become one, otherwise he won't get Moderation and has to
                    // wait
                    if (becomeModerator) {
                        currentClient.setIsMod(true);

                        // There is a need to send an extra Event here, cause at
                        // this moment there could be
                        // already somebody in the Room waiting

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

                        List<RoomClient> modRoomList = this.clientListManager
                                .getCurrentModeratorByRoom(currentClient.getRoom_id());

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

                } else {

                    // 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.clientListManager.updateClientByStreamId(streamid, currentClient);

                            List<RoomClient> modRoomList = this.clientListManager
                                    .getCurrentModeratorByRoom(currentClient.getRoom_id());

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

                    }

                }

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

            } else {

                // If this is an Appointment then the Moderator will be set to
                // the Invitor

                Appointment ment = appointmentLogic.getAppointmentByRoom(room_id);

                List<MeetingMember> members = meetingMemberDao
                        .getMeetingMemberByAppointmentId(ment.getAppointmentId());

                Long userIdInRoomClient = currentClient.getUser_id();

                boolean found = false;
                boolean moderator_set = false;

                // Check if current user is set to moderator
                for (int i = 0; i < members.size(); i++) {
                    MeetingMember member = members.get(i);

                    // only persistent users can schedule a meeting
                    // user-id is only set for registered users
                    if (member.getUserid() != null) {
                        log.debug("checking user " + member.getFirstname() + " for moderator role - ID : "
                                + member.getUserid().getUser_id());

                        if (member.getUserid().getUser_id().equals(userIdInRoomClient)) {
                            found = true;

                            if (member.getInvitor()) {
                                log.debug("User " + userIdInRoomClient
                                        + " is moderator due to flag in MeetingMember record");
                                currentClient.setIsMod(true);

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

                                List<RoomClient> modRoomList = this.clientListManager
                                        .getCurrentModeratorByRoom(currentClient.getRoom_id());

                                // There is a need to send an extra Event here, cause at this moment 
                                // there could be already somebody in the Room waiting

                                //Sync message to everybody
                                syncMessageToCurrentScope("setNewModeratorByList", modRoomList, false);

                                moderator_set = true;
                                this.clientListManager.updateClientByStreamId(streamid, currentClient);
                                break;
                            } else {
                                log.debug("User " + userIdInRoomClient
                                        + " is NOT moderator due to flag in MeetingMember record");
                                currentClient.setIsMod(false);
                                this.clientListManager.updateClientByStreamId(streamid, currentClient);
                                break;
                            }
                        } else {
                            if (member.getInvitor())
                                moderator_set = true;
                        }
                    } else {
                        if (member.getInvitor())
                            moderator_set = true;
                    }

                }

                if (!found) {
                    log.debug("User " + userIdInRoomClient
                            + " could not be found as MeetingMember -> definitely no moderator");
                    currentClient.setIsMod(false);
                    this.clientListManager.updateClientByStreamId(streamid, currentClient);
                } else {
                    // if current user is part of the member list, but moderator
                    // couldn't be retrieved : first come, first draw!
                    if (clientListRoom.size() == 1 && moderator_set == false) {
                        log.debug("");
                        currentClient.setIsMod(true);

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

                        List<RoomClient> modRoomList = this.clientListManager
                                .getCurrentModeratorByRoom(currentClient.getRoom_id());

                        // There is a need to send an extra Event here, cause at
                        // this moment there could be
                        // already somebody in the Room waiting

                        //Sync message to everybody
                        syncMessageToCurrentScope("setNewModeratorByList", modRoomList, false);

                        this.clientListManager.updateClientByStreamId(streamid, currentClient);
                    }
                }

            }

            //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.setClientMap(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
     */
    public synchronized RoomClient setUsernameReconnect(String SID, Long userId, String username, String firstname,
            String lastname, String picture_uri) {
        try {
            IConnection current = Red5.getConnectionLocal();
            String streamid = current.getClient().getId();
            RoomClient currentClient = this.clientListManager.getClientByStreamId(streamid);

            currentClient.setUsername(username);
            currentClient.setUser_id(userId);
            currentClient.setPicture_uri(picture_uri);
            currentClient.setUserObject(userId, username, firstname, lastname);

            // Update Session Data
            sessionManagement.updateUserWithoutSession(SID, userId);

            Users user = userManagement.getUserById(userId);

            if (user != null) {
                currentClient.setExternalUserId(user.getExternalUserId());
                currentClient.setExternalUserType(user.getExternalUserType());
            }

            // only fill this value from User-Record
            // cause invited users have non
            // you cannot set the firstname,lastname from the UserRecord
            Users us = usersDao.getUser(userId);
            if (us != null && us.getPictureuri() != null) {
                // set Picture-URI
                currentClient.setPicture_uri(us.getPictureuri());
            }
            this.clientListManager.updateClientByStreamId(streamid, currentClient);
            return currentClient;
        } catch (Exception err) {
            log.error("[setUsername]", err);
        }
        return null;
    }

    /**
     * this is set initial directly after login/loading language
     * 
     * @param userId
     * @param username
     * @param firstname
     * @param lastname
     * @param orgdomain
     * @return
     */
    public synchronized RoomClient setUsernameAndSession(String SID, Long userId, String username, String firstname,
            String lastname) {
        try {
            IConnection current = Red5.getConnectionLocal();
            String streamid = current.getClient().getId();
            RoomClient currentClient = this.clientListManager.getClientByStreamId(streamid);

            currentClient.setUsername(username);
            currentClient.setUser_id(userId);
            currentClient.setUserObject(userId, username, firstname, lastname);

            // Update Session Data
            log.debug("UDPATE SESSION " + SID + ", " + userId);
            sessionManagement.updateUserWithoutSession(SID, userId);

            Users user = userManagement.getUserById(userId);

            if (user != null) {
                currentClient.setExternalUserId(user.getExternalUserId());
                currentClient.setExternalUserType(user.getExternalUserType());
            }

            // only fill this value from User-Record
            // cause invited users have non
            // you cannot set the firstname,lastname from the UserRecord
            Users us = usersDao.getUser(userId);
            if (us != null && us.getPictureuri() != null) {
                // set Picture-URI
                currentClient.setPicture_uri(us.getPictureuri());
            }
            this.clientListManager.updateClientByStreamId(streamid, currentClient);
            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
     */
    public synchronized HashMap<String, RoomClient> sendMessageByRoomAndDomain(Long room_id, Object message) {
        HashMap<String, RoomClient> roomClientList = new HashMap<String, RoomClient>();
        try {

            log.debug("sendMessageByRoomAndDomain " + room_id);

            IScope globalScope = getContext().getGlobalScope();

            IScope webAppKeyScope = globalScope.getScope(OpenmeetingsVariables.webAppRootKey);

            log.debug("webAppKeyScope " + webAppKeyScope);

            IScope scopeHibernate = webAppKeyScope.getScope(room_id.toString());
            // IScope scopeHibernate = webAppKeyScope.getScope("hibernate");

            if (scopeHibernate != null) {

                Collection<Set<IConnection>> conCollection = webAppKeyScope.getScope(room_id.toString())
                        .getConnections();
                for (Set<IConnection> conset : conCollection) {
                    for (IConnection conn : conset) {
                        if (conn != null) {
                            RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());
                            if (rcl == null) {
                                // continue;
                            } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                                // continue;
                            } else {
                                if (conn instanceof IServiceCapableConnection) {
                                    // RoomClient rcl =
                                    // this.clientListManager.getClientByStreamId(conn.getClient().getId());
                                    ((IServiceCapableConnection) conn).invoke("newMessageByRoomAndDomain",
                                            new Object[] { message }, this);

                                    // log.debug("sending newMessageByRoomAndDomain to "
                                    // + conn);
                                }
                            }
                        }
                    }
                }

            } else {
                log.debug("sendMessageByRoomAndDomain servlet not yet started  - roomID : '" + room_id + "'");
            }

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

    public synchronized List<RoomClient> getCurrentModeratorList() {
        try {
            log.debug("*..*getCurrentModerator id: ");

            IConnection current = Red5.getConnectionLocal();
            RoomClient currentClient = this.clientListManager.getClientByStreamId(current.getClient().getId());
            Long room_id = currentClient.getRoom_id();

            // log.debug("Who is this moderator? "+currentMod);

            return this.clientListManager.getCurrentModeratorByRoom(room_id);
        } catch (Exception err) {
            log.error("[getCurrentModerator]", err);
        }
        return null;
    }

    /**
     * This Function is triggered from the Whiteboard
     * 
     * @param whiteboardObj
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public synchronized void sendVars(ArrayList whiteboardObjParam) {
        //
        try {

            // In previous version this has been always a Map, now its a List
            // so I re-wrapp that class to be a Map again.
            // swagner 13.02.2009
            // log.debug("*..*sendVars1: " + whiteboardObjParam);
            // log.debug("*..*sendVars2: " + whiteboardObjParam.getClass());
            // log.debug("*..*sendVars3: " +
            // whiteboardObjParam.getClass().getName());

            Map whiteboardObj = new HashMap();
            int i = 0;
            for (Iterator iter = whiteboardObjParam.iterator(); iter.hasNext();) {
                Object obj = iter.next();
                // log.debug("obj"+obj);
                whiteboardObj.put(i, obj);
                i++;
            }

            // Map whiteboardObj = (Map) whiteboardObjParam;

            // Check if this User is the Mod:
            IConnection current = Red5.getConnectionLocal();
            RoomClient currentClient = this.clientListManager.getClientByStreamId(current.getClient().getId());

            if (currentClient == null) {
                return;
            }

            Long room_id = currentClient.getRoom_id();

            String action = whiteboardObj.get(2).toString();

            // log.debug("***** sendVars: " + actionObject.get(0));

            if (action != null && action.equals("whiteboardObj")) {
                // Update Whiteboard Object
                List actionObject = (List) whiteboardObj.get(3);
                WhiteboardManagement.getInstance().updateWhiteboardObject(room_id, actionObject);
            } else if (action != null && action.equals("moveMap")) {
                // Update Whiteboard Object
                List actionObject = (List) whiteboardObj.get(3);
                WhiteboardManagement.getInstance().updateWhiteboardObjectPos(room_id, actionObject);
            } else {
                // Store event in list
                WhiteboardManagement.getInstance().addWhiteBoardObject(room_id, whiteboardObj);
            }

            boolean showDrawStatus = getWhiteboardDrawStatus();

            // Notify all Clients of that Scope (Room)
            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        if (conn instanceof IServiceCapableConnection) {

                            if (conn.getClient().getId().equals(current.getClient().getId())) {
                                continue;
                            }

                            RoomClient rcl = this.clientListManager
                                    .getSyncClientByStreamId(conn.getClient().getId());

                            if (rcl == null) {
                                continue;
                            }

                            if (!currentClient.getStreamid().equals(rcl.getStreamid())) {
                                ((IServiceCapableConnection) conn).invoke("sendVarsToWhiteboard",
                                        new Object[] { (showDrawStatus ? currentClient : null), whiteboardObj },
                                        this);
                            }
                        }
                    }
                }
            }

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

    /**
     * This Function is triggered from the Whiteboard
     * 
     * @param whiteboardObj
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public synchronized void sendVarsByWhiteboardId(ArrayList whiteboardObjParam, Long whiteboardId) {
        //
        try {

            Map whiteboardObj = new HashMap();
            int i = 0;
            for (Iterator iter = whiteboardObjParam.iterator(); iter.hasNext();) {
                Object obj = iter.next();
                // log.debug("obj"+obj);
                whiteboardObj.put(i, obj);
                i++;
            }

            // Check if this User is the Mod:
            IConnection current = Red5.getConnectionLocal();
            RoomClient currentClient = this.clientListManager.getClientByStreamId(current.getClient().getId());

            if (currentClient == null) {
                return;
            }

            Long room_id = currentClient.getRoom_id();

            // log.debug("***** sendVars: " + whiteboardObj);

            // Store event in list
            String action = whiteboardObj.get(2).toString();

            if (action.equals("deleteMindMapNodes")) {

                // Simulate Single Delete Events for z-Index
                List actionObject = (List) whiteboardObj.get(3);

                List<List> itemObjects = (List) actionObject.get(3);

                Map whiteboardTempObj = new HashMap();
                whiteboardTempObj.put(2, "delete");

                for (List itemObject : itemObjects) {

                    List<Object> tempActionObject = new LinkedList<Object>();
                    tempActionObject.add("mindmapnode");
                    tempActionObject.add(itemObject.get(0)); // z-Index -8
                    tempActionObject.add(null); // simulate -7
                    tempActionObject.add(null); // simulate -6
                    tempActionObject.add(null); // simulate -5
                    tempActionObject.add(null); // simulate -4
                    tempActionObject.add(null); // simulate -3
                    tempActionObject.add(null); // simulate -2
                    tempActionObject.add(itemObject.get(1)); // Object-Name -1

                    whiteboardTempObj.put(3, tempActionObject);

                    WhiteboardManagement.getInstance().addWhiteBoardObjectById(room_id, whiteboardTempObj,
                            whiteboardId);

                }

            } else {

                WhiteboardManagement.getInstance().addWhiteBoardObjectById(room_id, whiteboardObj, whiteboardId);

            }

            // This is no longer necessary
            // boolean ismod = currentClient.getIsMod();

            // log.debug("*..*ismod: " + ismod);

            // if (ismod) {

            Map<String, Object> sendObject = new HashMap<String, Object>();
            sendObject.put("id", whiteboardId);
            sendObject.put("param", whiteboardObjParam);

            boolean showDrawStatus = getWhiteboardDrawStatus();

            // Notify all Clients of that Scope (Room)
            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        if (conn instanceof IServiceCapableConnection) {
                            if (conn.getClient().getId().equals(current.getClient().getId())) {
                                continue;
                            }

                            RoomClient rcl = this.clientListManager
                                    .getSyncClientByStreamId(conn.getClient().getId());

                            if (rcl == null) {
                                continue;
                            }

                            if (!currentClient.getStreamid().equals(rcl.getStreamid())) {
                                ((IServiceCapableConnection) conn).invoke("sendVarsToWhiteboardById",
                                        new Object[] { showDrawStatus ? currentClient : null, sendObject }, this);
                            }
                        }
                    }
                }
            }

            // return numberOfUsers;
            // } else {
            // // log.debug("*..*you are not allowed to send: "+ismod);
            // return -1;
            // }
        } catch (Exception err) {
            log.error("[sendVarsByWhiteboardId]", err);
        }
    }

    public synchronized int sendVarsModeratorGeneral(Object vars) {
        log.debug("*..*sendVars: " + vars);
        try {
            IConnection current = Red5.getConnectionLocal();
            RoomClient currentClient = this.clientListManager.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
                Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
                for (Set<IConnection> conset : conCollection) {
                    for (IConnection conn : conset) {
                        if (conn != null) {
                            if (conn instanceof IServiceCapableConnection) {
                                RoomClient rcl = this.clientListManager
                                        .getClientByStreamId(conn.getClient().getId());
                                if (rcl == null) {
                                    // continue;
                                } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                                    // continue;
                                } else {
                                    // log.debug("*..*idremote: " +
                                    // rcl.getStreamid());
                                    // log.debug("*..*my idstreamid: " +
                                    // currentClient.getStreamid());
                                    if (!currentClient.getStreamid().equals(rcl.getStreamid())) {
                                        ((IServiceCapableConnection) conn).invoke("sendVarsToModeratorGeneral",
                                                new Object[] { vars }, this);
                                        // log.debug("sending sendVarsToModeratorGeneral to "
                                        // + conn);
                                    }
                                }
                            }
                        }
                    }
                }
                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
     */
    @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
     * @return
     */
    public synchronized int sendMessageToMembers(Object newMessage) {
        try {

            //Sync to all users of current scope
            syncMessageToCurrentScope("sendVarsToMessage", newMessage, false);

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

    /**
     * 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
     * @param newMessage
     * @param sendSelf 
     * @return
     */
    public synchronized int syncMessageToCurrentScope(String remoteMethodName, Object newMessage,
            boolean sendSelf) {
        try {
            IConnection current = Red5.getConnectionLocal();

            // Send to all Clients of that Scope(Room)
            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        if (conn instanceof IServiceCapableConnection) {
                            RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());

                            if (rcl == null) {
                                // RoomClient can be null if there are network problems
                                continue;
                            } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                                // screen sharing clients do not receive events
                                continue;
                            } else if (rcl.getIsAVClient()) {
                                // AVClients or potential AVClients do not receive events
                                continue;
                            } else if (current.getClient().getId().equals(conn.getClient().getId()) && !sendSelf) {
                                //Do not send back to self
                                continue;
                            }

                            ((IServiceCapableConnection) conn).invoke(remoteMethodName, new Object[] { newMessage },
                                    this);

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

    /**
     * wrapper method
     * @param newMessage
     * @return
     */
    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
     */
    public synchronized int sendMessageWithClientWithSyncObject(Object newMessage, boolean sync) {
        try {
            IConnection current = Red5.getConnectionLocal();
            RoomClient currentClient = this.clientListManager.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
     */
    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
            Collection<Set<IConnection>> conCollection = scope.getConnections();

            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        if (conn instanceof IServiceCapableConnection) {
                            RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());
                            if (rcl == null) {
                                // continue;
                            } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                                // continue;
                            } else {
                                // log.debug("### sendMessageById 1 ###"+clientId);
                                // log.debug("### sendMessageById 2 ###"+conn.getClient().getId());
                                if (conn.getClient().getId().equals(clientId)) {
                                    ((IServiceCapableConnection) conn).invoke("sendVarsToMessageWithClient",
                                            new Object[] { hsm }, this);
                                    // log.debug("sendingsendVarsToMessageWithClient ByID to "
                                    // + conn);
                                }
                            }
                        }
                    }
                }
            }
        } 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
     */
    public synchronized int sendMessageWithClientById(Object newMessage, String clientId) {
        try {
            IConnection current = Red5.getConnectionLocal();
            RoomClient currentClient = this.clientListManager.getClientByStreamId(current.getClient().getId());

            log.debug("### sendMessageWithClientById ###" + clientId);

            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
            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());
                        if (rcl == null) {
                            // continue;
                        } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                            // continue;
                        } else {
                            if (conn instanceof IServiceCapableConnection) {
                                // log.debug("### sendMessageWithClientById 1 ###"+clientId);
                                // log.debug("### sendMessageWithClientById 2 ###"+conn.getClient().getId());
                                if (conn.getClient().getId().equals(clientId)) {
                                    ((IServiceCapableConnection) conn).invoke("sendVarsToMessageWithClient",
                                            new Object[] { hsm }, this);
                                    // log.debug("sendingsendVarsToMessageWithClient ByID to "
                                    // + conn);
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception err) {
            log.error("[sendMessageWithClient] ", err);
            return -1;
        }
        return 1;
    }

    public synchronized void sendMessageWithClientByPublicSID(Object message, String publicSID) {
        try {
            // ApplicationContext appCtx = getContext().getApplicationContext();
            IScope globalScope = getContext().getGlobalScope();

            IScope webAppKeyScope = globalScope.getScope(OpenmeetingsVariables.webAppRootKey);

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

            // Get Room Id to send it to the correct Scope
            RoomClient currentClient = this.clientListManager.getClientByPublicSID(publicSID, false);

            if (currentClient == null) {
                throw new Exception("Could not Find RoomClient on List publicSID: " + publicSID);
            }
            // 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

                Collection<Set<IConnection>> conCollection = webAppKeyScope.getScope(scopeName).getConnections();
                for (Set<IConnection> conset : conCollection) {
                    for (IConnection conn : conset) {
                        if (conn != null) {
                            RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());
                            if (rcl != null) {
                                if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                                    // continue;
                                } else {
                                    // log.debug("rcl "+rcl+" rcl.getUser_id(): "+rcl.getPublicSID()+" publicSID: "+publicSID+
                                    // " IS EQUAL? "+rcl.getPublicSID().equals(publicSID));
                                    if (rcl.getPublicSID().equals(publicSID)) {
                                        // log.debug("IS EQUAL ");
                                        ((IServiceCapableConnection) conn).invoke("newMessageByRoomAndDomain",
                                                new Object[] { message }, this);
                                        log.debug("newMessageByRoomAndDomain RPC:newMessageByRoomAndDomain"
                                                + message);
                                    }
                                }
                            }
                        }
                    }
                }

            } else {
                // Scope not yet started
            }
        } catch (Exception err) {
            log.error("[sendMessageWithClient] ", err);
            err.printStackTrace();
        }
    }

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

            IScope globalScope = getContext().getGlobalScope();

            IScope webAppKeyScope = globalScope.getScope(OpenmeetingsVariables.webAppRootKey);

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

            // Get Room Id to send it to the correct Scope
            RoomClient currentClient = this.clientListManager.getClientByPublicSID(publicSID, false);

            if (currentClient == null) {
                currentClient = this.clientListManager.getClientByUserId(user_id);
            }

            Collection<Set<IConnection>> conCollection = 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)
                conCollection = 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) {
                    conCollection = webAppKeyScope.getScope(scopeName).getConnections();
                }
            }

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

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

            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());
                        if (rcl != null) {
                            if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                                // continue;
                            } else {
                                // log.debug("rcl "+rcl+" rcl.getUser_id(): "+rcl.getPublicSID()+" publicSID: "+publicSID+
                                // " IS EQUAL? "+rcl.getPublicSID().equals(publicSID));
                                if (rcl.getPublicSID().equals(publicSID)) {
                                    // log.debug("IS EQUAL ");
                                    ((IServiceCapableConnection) conn).invoke("newMessageByRoomAndDomain",
                                            new Object[] { message }, this);
                                    log.debug("sendMessageWithClientByPublicSID RPC:newMessageByRoomAndDomain"
                                            + message);
                                } else if (user_id != 0 && rcl.getUser_id() != null
                                        && rcl.getUser_id().equals(user_id)) {
                                    ((IServiceCapableConnection) conn).invoke("newMessageByRoomAndDomain",
                                            new Object[] { message }, this);
                                    log.debug("sendMessageWithClientByPublicSID RPC:newMessageByRoomAndDomain"
                                            + message);
                                }
                            }
                        }
                    }
                }
            }

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

    public synchronized Boolean getInterviewRecordingStatus() {
        try {

            IConnection current = Red5.getConnectionLocal();

            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {

                        RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());

                        if (rcl.getIsRecording() != null && rcl.getIsRecording()) {
                            return true;
                        }

                    }
                }
            }

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

        return null;
    }

    public synchronized Boolean startInterviewRecording() {
        try {

            IConnection current = Red5.getConnectionLocal();

            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {

                        RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());

                        if (rcl.getIsRecording() != null && rcl.getIsRecording()) {
                            return false;
                        }

                    }
                }
            }

            RoomClient current_rcl = this.clientListManager.getClientByStreamId(current.getClient().getId());

            // Also set the Recording Flag to Record all Participants that enter
            // later
            current_rcl.setIsRecording(true);
            this.clientListManager.updateClientByStreamId(current.getClient().getId(), current_rcl);

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

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

                        RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());

                        if (rcl == null) {
                            continue;
                        } else if (rcl.getIsAVClient()) {
                            continue;
                        } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {
                            continue;
                        }

                        ((IServiceCapableConnection) conn).invoke("interviewStatus",
                                new Object[] { interviewStatus }, this);
                        log.debug("-- interviewStatus" + interviewStatus);

                    }
                }
            }

            String recordingName = "Interview " + CalendarPatterns.getDateWithTimeByMiliSeconds(new Date());

            this.flvRecorderService.recordMeetingStream(recordingName, "", true);

            return true;

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

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

            IConnection current = Red5.getConnectionLocal();

            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {

                        RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());

                        if (rcl == null) {
                            // continue;
                        } else if (rcl.getIsScreenClient() != null && rcl.getIsScreenClient()) {

                            if (rcl.getStreamid() != null && rcl.getStreamid().equals(streamid)) {
                                ((IServiceCapableConnection) conn).invoke("sendRemoteCursorEvent",
                                        new Object[] { messageObj }, this);
                                log.debug("sendRemoteCursorEvent messageObj" + messageObj);
                            }

                        }

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

    //FIXME: legacy code, needs to be removed
    public boolean checkSharerSession() {
        return true;
    }

    /**
     * Stop the recording of the streams and send event to connected users of scope
     * 
     * @return
     */
    public synchronized Boolean stopInterviewRecording() {
        try {

            IConnection current = Red5.getConnectionLocal();

            boolean found = false;
            Long flvRecordingId = null;

            Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {

                        RoomClient rcl = this.clientListManager.getClientByStreamId(conn.getClient().getId());

                        if (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
                            this.clientListManager.updateClientByStreamId(conn.getClient().getId(), rcl);

                            found = true;
                        }

                    }
                }
            }

            if (!found) {
                return false;
            }

            RoomClient currentClient = this.clientListManager.getClientByStreamId(current.getClient().getId());

            this.flvRecorderService.stopRecordAndSave(scope, currentClient, flvRecordingId);

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

            for (Set<IConnection> conset : conCollection) {
                for (IConnection conn : conset) {
                    if (conn != null) {
                        ((IServiceCapableConnection) conn).invoke("interviewStatus",
                                new Object[] { interviewStatus }, this);
                        log.debug("sendMessageWithClientByPublicSID interviewStatus" + interviewStatus);

                    }
                }
            }

            return true;

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

    /**
     * Get all ClientList Objects of that room and domain Used in
     * lz.applyForModeration.lzx
     * 
     * @return
     */
    public synchronized HashMap<String, RoomClient> getClientListScope() {
        HashMap<String, RoomClient> roomClientList = new HashMap<String, RoomClient>();
        try {
            IConnection current = Red5.getConnectionLocal();
            RoomClient currentClient = this.clientListManager.getClientByStreamId(current.getClient().getId());

            return this.clientListManager.getClientListByRoom(currentClient.getRoom_id());

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

    private boolean getWhiteboardDrawStatus() {
        if (ScopeApplicationAdapter.whiteboardDrawStatus == null) {
            String drawStatus = cfgManagement.getConfValue("show.whiteboard.draw.status", String.class, "0");
            ScopeApplicationAdapter.whiteboardDrawStatus = "1".equals(drawStatus);
        }
        return ScopeApplicationAdapter.whiteboardDrawStatus;
    }

    public String getCryptKey() {
        try {

            if (ScopeApplicationAdapter.configKeyCryptClassName == null) {
                Configuration conf = cfgManagement.getConfKey(3, "crypt_ClassName");

                if (conf != null) {
                    ScopeApplicationAdapter.configKeyCryptClassName = conf.getConf_value();
                }
            }

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

    public String getExclusiveAudioKeyCode() {
        Configuration conf = cfgManagement.getConfKey(3, "exclusive.audio.keycode");
        if (null != conf) {
            return conf.getConf_value();
        } else {
            return null;
        }
    }

    public synchronized IScope getRoomScope(String room) {
        try {

            IScope globalScope = getContext().getGlobalScope();
            IScope webAppKeyScope = globalScope.getScope(OpenmeetingsVariables.webAppRootKey);

            String scopeName = "hibernate";
            // If set then its a NON default Scope
            if (room.length() != 0) {
                scopeName = room;
            }

            IScope scopeHibernate = webAppKeyScope.getScope(scopeName);

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

    /*
    * SIP transport methods
    */

    public synchronized void updateSipTransport() {
        IConnection current = Red5.getConnectionLocal();
        String streamid = current.getClient().getId();
        RoomClient currentClient = this.clientListManager.getClientByStreamId(streamid);
        log.debug("getSipConferenceMembersNumber: "
                + roommanagement.getSipConferenceMembersNumber(currentClient.getRoom_id()));
        String newNumber = "("
                + Integer.toString(roommanagement.getSipConferenceMembersNumber(currentClient.getRoom_id()) - 1)
                + ")";
        if (!newNumber.equals(currentClient.getLastname())) {
            currentClient.setLastname(newNumber);
            this.clientListManager.updateClientByStreamId(streamid, currentClient);
            log.debug("updateSipTransport: {}, {}, {}, {}", new Object[] { currentClient.getPublicSID(),
                    currentClient.getRoom_id(), currentClient.getFirstname(), currentClient.getLastname() });
            sendMessageWithClient(
                    new String[] { "personal", currentClient.getFirstname(), currentClient.getLastname() });
        }
    }

    /**
     * Perform call to specified phone number and join to conference
     * @param number to call
     */
    public synchronized void joinToConfCall(String number) {
        IConnection current = Red5.getConnectionLocal();
        String streamid = current.getClient().getId();
        RoomClient currentClient = this.clientListManager.getClientByStreamId(streamid);
        Rooms rooms = roommanagement.getRoomById(currentClient.getRoom_id());
        log.debug("asterisk -rx \"originate Local/" + number + "@rooms extension " + rooms.getSipNumber()
                + "@rooms\"");
        try {
            Runtime.getRuntime().exec(new String[] { "asterisk", "-rx",
                    "originate Local/" + number + "@rooms extension " + rooms.getSipNumber() + "@rooms" });
        } catch (IOException e) {
            log.error("Executing asterisk originate error: ", e);
        }
    }

    public synchronized String getSipNumber(Long room_id) {
        Rooms rooms = roommanagement.getRoomById(room_id);
        if (rooms != null) {
            log.debug("getSipNumber: room_id: {}, sipNumber: {}", new Object[] { room_id, rooms.getSipNumber() });
            return rooms.getSipNumber();
        }
        return null;
    }

    public synchronized void setSipTransport(Long room_id, String publicSID, String broadCastId) {
        IConnection current = Red5.getConnectionLocal();
        String streamid = current.getClient().getId();
        // Notify all clients of the same scope (room)
        RoomClient currentClient = this.clientListManager.getClientByStreamId(streamid);
        currentClient.setRoom_id(room_id);
        currentClient.setRoomEnter(new Date());
        currentClient.setFirstname("SIP Transport");
        currentClient.setLastname(
                "(" + Integer.toString(roommanagement.getSipConferenceMembersNumber(room_id) - 1) + ")");
        currentClient.setBroadCastID(Long.parseLong(broadCastId));
        currentClient.setIsBroadcasting(true);
        currentClient.setPublicSID(publicSID);
        currentClient.setAvsettings("av");
        currentClient.setVWidth(120);
        currentClient.setVHeight(90);
        this.clientListManager.updateClientByStreamId(streamid, currentClient);

        Collection<Set<IConnection>> conCollection = current.getScope().getConnections();
        for (Set<IConnection> conset : conCollection) {
            for (IConnection conn : conset) {
                if (conn != null) {
                    RoomClient rcl = this.clientListManager.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
                            if (conn instanceof IServiceCapableConnection) {
                                ((IServiceCapableConnection) conn).invoke("addNewUser",
                                        new Object[] { currentClient }, this);
                                ((IServiceCapableConnection) conn).invoke("newStream",
                                        new Object[] { currentClient }, this);
                                log.debug("sending setSipTransport to " + conn);
                            }
                        }
                    }
                }
            }
        }
    }
}