Package org.openmeetings.app.remote.red5

Source Code of org.openmeetings.app.remote.red5.ScopeApplicationAdapter

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

Related Classes of org.openmeetings.app.remote.red5.ScopeApplicationAdapter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.