/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.api;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import org.bigbluebutton.api.domain.*;
import org.bigbluebutton.api.messaging.MessageListener;
import org.bigbluebutton.api.messaging.MessagingService;
import org.bigbluebutton.api.messaging.messages.CreateBreakoutRoom;
import org.bigbluebutton.api.messaging.messages.CreateMeeting;
import org.bigbluebutton.api.messaging.messages.EndBreakoutRoom;
import org.bigbluebutton.api.messaging.messages.EndMeeting;
import org.bigbluebutton.api.messaging.messages.IMessage;
import org.bigbluebutton.api.messaging.messages.MeetingDestroyed;
import org.bigbluebutton.api.messaging.messages.MeetingEnded;
import org.bigbluebutton.api.messaging.messages.MeetingStarted;
import org.bigbluebutton.api.messaging.messages.RegisterUser;
import org.bigbluebutton.api.messaging.messages.RemoveExpiredMeetings;
import org.bigbluebutton.api.messaging.messages.UserJoined;
import org.bigbluebutton.api.messaging.messages.UserJoinedVoice;
import org.bigbluebutton.api.messaging.messages.UserLeft;
import org.bigbluebutton.api.messaging.messages.UserLeftVoice;
import org.bigbluebutton.api.messaging.messages.UserListeningOnly;
import org.bigbluebutton.api.messaging.messages.UserSharedWebcam;
import org.bigbluebutton.api.messaging.messages.UserStatusChanged;
import org.bigbluebutton.api.messaging.messages.UserUnsharedWebcam;
import org.bigbluebutton.presentation.PresentationUrlDownloadService;
import org.bigbluebutton.api.messaging.messages.StunTurnInfoRequested;
import org.bigbluebutton.web.services.ExpiredMeetingCleanupTimerTask;
import org.bigbluebutton.web.services.RegisteredUserCleanupTimerTask;
import org.bigbluebutton.web.services.turn.StunServer;
import org.bigbluebutton.web.services.turn.StunTurnService;
import org.bigbluebutton.web.services.turn.TurnEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
public class MeetingService implements MessageListener {
private static Logger log = LoggerFactory.getLogger(MeetingService.class);
private BlockingQueue<IMessage> receivedMessages = new LinkedBlockingQueue<IMessage>();
private volatile boolean processMessage = false;
private final Executor msgProcessorExec = Executors
.newSingleThreadExecutor();
private final Executor runExec = Executors.newSingleThreadExecutor();
/**
* http://ria101.wordpress.com/2011/12/12/concurrenthashmap-avoid-a-common-
* misuse/
*/
private final ConcurrentMap<String, Meeting> meetings;
private final ConcurrentMap<String, UserSession> sessions;
private int defaultMeetingExpireDuration = 1;
private int defaultMeetingCreateJoinDuration = 5;
private RecordingService recordingService;
private MessagingService messagingService;
private ExpiredMeetingCleanupTimerTask cleaner;
private RegisteredUserCleanupTimerTask registeredUserCleaner;
private StunTurnService stunTurnService;
private boolean removeMeetingWhenEnded = false;
private ParamsProcessorUtil paramsProcessorUtil;
private PresentationUrlDownloadService presDownloadService;
public MeetingService() {
meetings = new ConcurrentHashMap<String, Meeting>(8, 0.9f, 1);
sessions = new ConcurrentHashMap<String, UserSession>(8, 0.9f, 1);
}
public void addUserSession(String token, UserSession user) {
sessions.put(token, user);
}
public void registerUser(String meetingID, String internalUserId,
String fullname, String role, String externUserID,
String authToken, String avatarURL) {
handle(new RegisterUser(meetingID, internalUserId, fullname, role,
externUserID, authToken, avatarURL));
}
public UserSession getUserSession(String token) {
return sessions.get(token);
}
public UserSession removeUserSession(String token) {
UserSession user = sessions.remove(token);
if (user != null) {
log.debug("Found user [" + user.fullname + "] token=[" + token
+ "] to meeting [" + user.meetingID + "]");
}
return user;
}
/**
* Remove the meetings that have ended from the list of running meetings.
*/
public void removeExpiredMeetings() {
handle(new RemoveExpiredMeetings());
}
/**
* Remove registered users who did not successfully joined the meeting.
*/
public void purgeRegisteredUsers() {
for (AbstractMap.Entry<String, Meeting> entry : this.meetings
.entrySet()) {
Long now = System.nanoTime();
Meeting meeting = entry.getValue();
ConcurrentMap<String, User> users = meeting.getUsersMap();
for (AbstractMap.Entry<String, Long> registeredUser : meeting
.getRegisteredUsers().entrySet()) {
String registeredUserID = registeredUser.getKey();
Long registeredUserDate = registeredUser.getValue();
long registrationTime = registeredUserDate.longValue();
long elapsedTime = now - registrationTime;
if (elapsedTime >= 60000
&& !users.containsKey(registeredUserID)) {
meeting.userUnregistered(registeredUserID);
}
}
}
handle(new RemoveExpiredMeetings());
}
private void kickOffProcessingOfRecording(Meeting m) {
if (m.isRecord() && m.getNumUsers() == 0) {
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", m.getInternalId());
logData.put("externalMeetingId", m.getExternalId());
logData.put("name", m.getName());
logData.put("event", "kick_off_ingest_and_processing");
logData.put("description", "Start processing of recording.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("Initiate recording processing: data={}", logStr);
processRecording(m.getInternalId());
}
}
private void processMeetingForRemoval(Meeting m) {
kickOffProcessingOfRecording(m);
destroyMeeting(m.getInternalId());
meetings.remove(m.getInternalId());
removeUserSessions(m.getInternalId());
}
private void removeUserSessions(String meetingId) {
Iterator<Map.Entry<String, UserSession>> iterator = sessions.entrySet()
.iterator();
while (iterator.hasNext()) {
Map.Entry<String, UserSession> entry = iterator.next();
UserSession userSession = entry.getValue();
if (userSession.meetingID.equals(meetingId)) {
iterator.remove();
}
}
}
private void checkAndRemoveExpiredMeetings() {
for (Meeting m : meetings.values()) {
if (m.hasExpired(defaultMeetingExpireDuration)) {
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", m.getInternalId());
logData.put("externalMeetingId", m.getExternalId());
logData.put("name", m.getName());
logData.put("event", "removing_meeting");
logData.put("description", "Meeting has expired.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("Removing expired meeting: data={}", logStr);
processMeetingForRemoval(m);
continue;
}
if (m.isForciblyEnded()) {
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", m.getInternalId());
logData.put("externalMeetingId", m.getExternalId());
logData.put("name", m.getName());
logData.put("event", "removing_meeting");
logData.put("description", "Meeting forcefully ended.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("Removing ended meeting: data={}", logStr);
processMeetingForRemoval(m);
continue;
}
if (m.wasNeverJoined(defaultMeetingCreateJoinDuration)) {
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", m.getInternalId());
logData.put("externalMeetingId", m.getExternalId());
logData.put("name", m.getName());
logData.put("event", "removing_meeting");
logData.put("description", "Meeting has not been joined.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("Removing un-joined meeting: data={}", logStr);
destroyMeeting(m.getInternalId());
meetings.remove(m.getInternalId());
removeUserSessions(m.getInternalId());
continue;
}
if (m.hasExceededDuration()) {
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", m.getInternalId());
logData.put("externalMeetingId", m.getExternalId());
logData.put("name", m.getName());
logData.put("event", "removing_meeting");
logData.put("description", "Meeting exceeded duration.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("Removing past duration meeting: data={}", logStr);
endMeeting(m.getInternalId());
}
}
}
private void destroyMeeting(String meetingID) {
messagingService.destroyMeeting(meetingID);
}
public Collection<Meeting> getMeetings() {
return meetings.isEmpty() ? Collections.<Meeting> emptySet()
: Collections.unmodifiableCollection(meetings.values());
}
public Collection<UserSession> getSessions() {
return sessions.isEmpty() ? Collections.<UserSession> emptySet()
: Collections.unmodifiableCollection(sessions.values());
}
public synchronized boolean createMeeting(Meeting m) {
String internalMeetingId = paramsProcessorUtil.convertToInternalMeetingId(m.getExternalId());
Meeting existing = getNotEndedMeetingWithId(internalMeetingId);
if (existing == null) {
meetings.put(m.getInternalId(), m);
handle(new CreateMeeting(m));
return true;
}
return false;
}
private void handleCreateMeeting(Meeting m) {
if (m.isBreakout()){
Meeting parent = meetings.get(m.getParentMeetingId());
parent.addBreakoutRoom(m.getExternalId());
if (parent.isRecord()) {
messagingService.addBreakoutRoom(parent.getInternalId(), m.getInternalId());
}
}
if (m.isRecord()) {
Map<String, String> metadata = new TreeMap<String, String>();
metadata.putAll(m.getMetadata());
// TODO: Need a better way to store these values for recordings
metadata.put("meetingId", m.getExternalId());
metadata.put("meetingName", m.getName());
metadata.put("isBreakout", m.isBreakout().toString());
messagingService.recordMeetingInfo(m.getInternalId(), metadata);
if (m.isBreakout()) {
Map<String, String> breakoutMetadata = new TreeMap<String, String>();
breakoutMetadata.put("meetingId", m.getExternalId());
breakoutMetadata.put("sequence", m.getSequence().toString());
breakoutMetadata.put("parentMeetingId", m.getParentMeetingId());
messagingService.recordBreakoutInfo(m.getInternalId(), breakoutMetadata);
}
}
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", m.getInternalId());
logData.put("externalMeetingId", m.getExternalId());
if (m.isBreakout()){
logData.put("sequence", m.getSequence());
logData.put("parentMeetingId", m.getParentMeetingId());
}
logData.put("name", m.getName());
logData.put("duration", m.getDuration());
logData.put("isBreakout", m.isBreakout());
logData.put("record", m.isRecord());
logData.put("event", "create_meeting");
logData.put("description", "Create meeting.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("Create meeting: data={}", logStr);
messagingService.createMeeting(m.getInternalId(), m.getExternalId(),
m.getParentMeetingId(), m.getName(), m.isRecord(),
m.getTelVoice(), m.getDuration(), m.getAutoStartRecording(),
m.getAllowStartStopRecording(), m.getWebcamsOnlyForModerator(),
m.getModeratorPassword(), m.getViewerPassword(),
m.getCreateTime(), formatPrettyDate(m.getCreateTime()),
m.isBreakout(), m.getSequence());
}
private String formatPrettyDate(Long timestamp) {
return new Date(timestamp).toString();
}
private void processCreateMeeting(CreateMeeting message) {
handleCreateMeeting(message.meeting);
}
private void processRegisterUser(RegisterUser message) {
messagingService.registerUser(message.meetingID,
message.internalUserId, message.fullname, message.role,
message.externUserID, message.authToken, message.avatarURL);
}
public String addSubscription(String meetingId, String event,
String callbackURL) {
String sid = messagingService.storeSubscription(meetingId, event,
callbackURL);
return sid;
}
public boolean removeSubscription(String meetingId, String subscriptionId) {
return messagingService.removeSubscription(meetingId, subscriptionId);
}
public List<Map<String, String>> listSubscriptions(String meetingId) {
return messagingService.listSubscriptions(meetingId);
}
public Meeting getMeeting(String meetingId) {
if (meetingId == null)
return null;
for (String key : meetings.keySet()) {
if (key.startsWith(meetingId))
return (Meeting) meetings.get(key);
}
return null;
}
public Collection<Meeting> getMeetingsWithId(String meetingId) {
if (meetingId == null)
return Collections.<Meeting> emptySet();
Collection<Meeting> m = new HashSet<Meeting>();
for (String key : meetings.keySet()) {
if (key.startsWith(meetingId))
m.add(meetings.get(key));
}
return m;
}
public Meeting getNotEndedMeetingWithId(String meetingId) {
if (meetingId == null)
return null;
for (String key : meetings.keySet()) {
if (key.startsWith(meetingId)) {
Meeting m = (Meeting) meetings.get(key);
if (!m.isForciblyEnded())
return m;
}
}
return null;
}
public List<RecordingMetadata> getRecordingsMetadata(List<String> idList, List<String> states) {
List<RecordingMetadata> recsList = recordingService.getRecordingsMetadata(idList, states);
return recsList;
}
public Map<String, Recording> getRecordings(List<String> idList, List<String> states) {
List<Recording> recsList = recordingService.getRecordings(idList, states);
Map<String, Recording> recs = reorderRecordings(recsList);
return recs;
}
public List<RecordingMetadata> filterRecordingsByMetadata(List<RecordingMetadata> recsList,
Map<String, String> metadataFilters) {
return recordingService.filterRecordingsByMetadata(recsList, metadataFilters);
}
public Map<String, Recording> filterRecordingsByMetadata(Map<String, Recording> recordings,
Map<String, String> metadataFilters) {
return recordingService.filterRecordingsByMetadata(recordings, metadataFilters);
}
public Map<String, Recording> reorderRecordings(List<Recording> olds) {
Map<String, Recording> map = new HashMap<String, Recording>();
for (Recording r : olds) {
if (!map.containsKey(r.getId())) {
Map<String, String> meta = r.getMetadata();
String mid = meta.remove("meetingId");
String name = meta.remove("meetingName");
r.setMeetingID(mid);
r.setName(name);
List<Playback> plays = new ArrayList<Playback>();
if (r.getPlaybackFormat() != null) {
plays.add(new Playback(r.getPlaybackFormat(), r.getPlaybackLink(),
getDurationRecording(r.getPlaybackDuration(), r.getEndTime(),
r.getStartTime()), r.getPlaybackExtensions()));
}
r.setPlaybacks(plays);
map.put(r.getId(), r);
} else {
Recording rec = map.get(r.getId());
rec.getPlaybacks().add(new Playback(r.getPlaybackFormat(), r.getPlaybackLink(),
getDurationRecording(r.getPlaybackDuration(), r.getEndTime(),
r.getStartTime()), r.getPlaybackExtensions()));
}
}
return map;
}
private int getDurationRecording(String playbackDuration, String end,
String start) {
int duration;
try {
if (!playbackDuration.equals("")) {
duration = (int) Math
.ceil((Long.parseLong(playbackDuration)) / 60000.0);
} else {
duration = (int) Math.ceil((Long.parseLong(end) - Long
.parseLong(start)) / 60000.0);
}
} catch (Exception e) {
log.debug(e.getMessage());
duration = 0;
}
return duration;
}
public boolean existsAnyRecording(List<String> idList) {
return recordingService.existAnyRecording(idList);
}
public void setPublishRecording(List<String> idList, boolean publish) {
for (String id : idList) {
if (publish) {
recordingService.changeState(id, Recording.STATE_PUBLISHED);
} else {
recordingService.changeState(id, Recording.STATE_UNPUBLISHED);
}
}
}
public void deleteRecordings(List<String> idList) {
for (String id : idList) {
recordingService.changeState(id, Recording.STATE_DELETED);
}
}
public void updateRecordings(List<String> idList, Map<String, String> metaParams) {
recordingService.updateMetaParams(idList, metaParams);
}
public void processRecording(String meetingId) {
recordingService.startIngestAndProcessing(meetingId);
}
public boolean isMeetingWithVoiceBridgeExist(String voiceBridge) {
/*
* Collection<Meeting> confs = meetings.values(); for (Meeting c :
* confs) { if (voiceBridge == c.getVoiceBridge()) { return true; } }
*/return false;
}
public void send(String channel, String message) {
messagingService.send(channel, message);
}
public void createdPolls(String meetingId, String title, String question,
String questionType, ArrayList<String> answers) {
messagingService.sendPolls(meetingId, title, question, questionType,
answers);
}
public void endMeeting(String meetingId) {
handle(new EndMeeting(meetingId));
}
private void processCreateBreakoutRoom(CreateBreakoutRoom message) {
Meeting parentMeeting = getMeeting(message.parentMeetingId);
if (parentMeeting != null) {
Map<String, String> params = new HashMap<String, String>();
params.put("name", message.name);
params.put("meetingID", message.meetingId);
params.put("parentMeetingID", message.parentMeetingId);
params.put("isBreakout", "true");
params.put("sequence", message.sequence.toString());
params.put("attendeePW", message.viewerPassword);
params.put("moderatorPW", message.moderatorPassword);
params.put("voiceBridge", message.voiceConfId);
params.put("duration", message.durationInMinutes.toString());
params.put("record", message.record.toString());
params.put("welcome", getMeeting(message.parentMeetingId)
.getWelcomeMessageTemplate());
Map<String, String> parentMeetingMetadata = parentMeeting
.getMetadata();
String metaPrefix = "meta_";
for (String key : parentMeetingMetadata.keySet()) {
String metaName = metaPrefix + key;
// Inject metadata from parent meeting into the breakout room.
params.put(metaName, parentMeetingMetadata.get(key));
}
Meeting breakout = paramsProcessorUtil.processCreateParams(params);
createMeeting(breakout);
presDownloadService.extractPresentationPage(message.parentMeetingId,
message.sourcePresentationId,
message.sourcePresentationSlide, breakout.getInternalId());
} else {
log.error(
"Failed to create breakout room {}.Reason: Parent meeting {} not found.",
message.meetingId, message.parentMeetingId);
}
}
private void processEndBreakoutRoom(EndBreakoutRoom message) {
processEndMeeting(new EndMeeting(message.breakoutMeetingId));
}
private void processEndMeeting(EndMeeting message) {
messagingService.endMeeting(message.meetingId);
Meeting m = getMeeting(message.meetingId);
if (m != null) {
m.setForciblyEnded(true);
if (removeMeetingWhenEnded) {
processRecording(m.getInternalId());
destroyMeeting(m.getInternalId());
meetings.remove(m.getInternalId());
removeUserSessions(m.getInternalId());
}
}
}
public void addUserCustomData(String meetingId, String userID,
Map<String, String> userCustomData) {
Meeting m = getMeeting(meetingId);
if (m != null) {
m.addUserCustomData(userID, userCustomData);
}
}
private void meetingStarted(MeetingStarted message) {
Meeting m = getMeeting(message.meetingId);
if (m != null) {
if (m.getStartTime() == 0) {
long now = System.currentTimeMillis();
m.setStartTime(now);
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", m.getInternalId());
logData.put("externalMeetingId", m.getExternalId());
if (m.isBreakout()) {
logData.put("parentMeetingId", m.getParentMeetingId());
}
logData.put("name", m.getName());
logData.put("duration", m.getDuration());
logData.put("record", m.isRecord());
logData.put("isBreakout", m.isBreakout());
logData.put("event", "meeting_started");
logData.put("description", "Meeting has started.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("Meeting started: data={}", logStr);
} else {
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", m.getInternalId());
logData.put("externalMeetingId", m.getExternalId());
if (m.isBreakout()) {
logData.put("parentMeetingId", m.getParentMeetingId());
}
logData.put("name", m.getName());
logData.put("duration", m.getDuration());
logData.put("record", m.isRecord());
logData.put("isBreakout", m.isBreakout());
logData.put("event", "meeting_restarted");
logData.put("description", "Meeting has restarted.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("Meeting restarted: data={}", logStr);
}
return;
}
}
private void meetingDestroyed(MeetingDestroyed message) {
Meeting m = getMeeting(message.meetingId);
if (m != null) {
long now = System.currentTimeMillis();
m.setEndTime(now);
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", m.getInternalId());
logData.put("externalMeetingId", m.getExternalId());
logData.put("name", m.getName());
logData.put("duration", m.getDuration());
logData.put("record", m.isRecord());
logData.put("event", "meeting_destroyed");
logData.put("description", "Meeting has been destroyed.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("Meeting destroyed: data={}", logStr);
return;
}
}
private void meetingEnded(MeetingEnded message) {
Meeting m = getMeeting(message.meetingId);
if (m != null) {
long now = System.currentTimeMillis();
m.setEndTime(now);
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", m.getInternalId());
logData.put("externalMeetingId", m.getExternalId());
logData.put("name", m.getName());
logData.put("duration", m.getDuration());
logData.put("record", m.isRecord());
logData.put("event", "meeting_destroyed");
logData.put("description", "Meeting has been destroyed.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("Meeting destroyed: data={}", logStr);
return;
}
}
private void userJoined(UserJoined message) {
Meeting m = getMeeting(message.meetingId);
if (m != null) {
if (m.getNumUsers() == 0) {
// First user joins the meeting. Reset the end time to zero
// in case the meeting has been rejoined.
m.setEndTime(0);
}
User user = new User(message.userId, message.externalUserId,
message.name, message.role, message.avatarURL);
m.userJoined(user);
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", m.getInternalId());
logData.put("externalMeetingId", m.getExternalId());
logData.put("name", m.getName());
logData.put("userId", message.userId);
logData.put("externalUserId", user.getExternalUserId());
logData.put("username", user.getFullname());
logData.put("role", user.getRole());
logData.put("event", "user_joined_message");
logData.put("description", "User had joined the meeting.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("User joined meeting: data={}", logStr);
return;
}
return;
}
private void userLeft(UserLeft message) {
Meeting m = getMeeting(message.meetingId);
if (m != null) {
User user = m.userLeft(message.userId);
if (user != null) {
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", m.getInternalId());
logData.put("externalMeetingId", m.getExternalId());
logData.put("name", m.getName());
logData.put("userId", message.userId);
logData.put("externalUserId", user.getExternalUserId());
logData.put("username", user.getFullname());
logData.put("role", user.getRole());
logData.put("event", "user_left_message");
logData.put("description", "User had left the meeting.");
Gson gson = new Gson();
String logStr = gson.toJson(logData);
log.info("User left meeting: data={}", logStr);
if (m.getNumUsers() == 0) {
// Last user the meeting. Mark this as the time
// the meeting ended.
m.setEndTime(System.currentTimeMillis());
}
Long userRegistered = m.userUnregistered(message.userId);
if (userRegistered != null) {
log.info("User unregistered from meeting");
} else {
log.info("User was not unregistered from meeting because it was not found");
}
return;
}
return;
}
}
private void updatedStatus(UserStatusChanged message) {
Meeting m = getMeeting(message.meetingId);
if (m != null) {
User user = m.getUserById(message.userId);
if (user != null) {
user.setStatus(message.status, message.value);
return;
}
return;
}
}
@Override
public void handle(IMessage message) {
receivedMessages.add(message);
}
public void setParamsProcessorUtil(ParamsProcessorUtil util) {
this.paramsProcessorUtil = util;
}
public void setPresDownloadService(
PresentationUrlDownloadService presDownloadService) {
this.presDownloadService = presDownloadService;
}
private void processStunTurnInfoRequested(StunTurnInfoRequested message) {
Set<StunServer> stuns = stunTurnService.getStunServers();
log.info("\nhere are the stuns:");
for (StunServer s : stuns) {
log.info("a stun: " + s.url);
}
Set<TurnEntry> turns = stunTurnService
.getStunAndTurnServersFor(message.internalUserId);
log.info("\nhere are the (" + turns.size()
+ ") turns for internalUserId:" + message.internalUserId);
for (TurnEntry t : turns) {
log.info("a turn: " + t.url + "username/pass=" + t.username + '/'
+ t.password);
}
messagingService.sendStunTurnInfo(message.meetingId,
message.internalUserId, stuns, turns);
}
public void userJoinedVoice(UserJoinedVoice message) {
Meeting m = getMeeting(message.meetingId);
if (m != null) {
User user = m.getUserById(message.userId);
if (user != null) {
user.setVoiceJoined(true);
return;
}
return;
}
}
public void userLeftVoice(UserLeftVoice message) {
Meeting m = getMeeting(message.meetingId);
if (m != null) {
User user = m.getUserById(message.userId);
if (user != null) {
user.setVoiceJoined(false);
return;
}
return;
}
}
public void userListeningOnly(UserListeningOnly message) {
Meeting m = getMeeting(message.meetingId);
if (m != null) {
User user = m.getUserById(message.userId);
if (user != null) {
user.setListeningOnly(message.listenOnly);
return;
}
return;
}
}
public void userSharedWebcam(UserSharedWebcam message) {
Meeting m = getMeeting(message.meetingId);
if (m != null) {
User user = m.getUserById(message.userId);
if (user != null) {
user.addStream(message.stream);
return;
}
return;
}
}
public void userUnsharedWebcam(UserUnsharedWebcam message) {
Meeting m = getMeeting(message.meetingId);
if (m != null) {
User user = m.getUserById(message.userId);
if (user != null) {
user.removeStream(message.stream);
return;
}
return;
}
}
private void processMessage(final IMessage message) {
Runnable task = new Runnable() {
public void run() {
if (message instanceof MeetingStarted) {
meetingStarted((MeetingStarted) message);
} else if (message instanceof MeetingDestroyed) {
meetingDestroyed((MeetingDestroyed) message);
} else if (message instanceof MeetingEnded) {
meetingEnded((MeetingEnded) message);
} else if (message instanceof UserJoined) {
userJoined((UserJoined) message);
} else if (message instanceof UserLeft) {
userLeft((UserLeft) message);
} else if (message instanceof UserStatusChanged) {
updatedStatus((UserStatusChanged) message);
} else if (message instanceof UserJoinedVoice) {
userJoinedVoice((UserJoinedVoice) message);
} else if (message instanceof UserLeftVoice) {
userLeftVoice((UserLeftVoice) message);
} else if (message instanceof UserListeningOnly) {
userListeningOnly((UserListeningOnly) message);
} else if (message instanceof UserSharedWebcam) {
userSharedWebcam((UserSharedWebcam) message);
} else if (message instanceof UserUnsharedWebcam) {
userUnsharedWebcam((UserUnsharedWebcam) message);
} else if (message instanceof RemoveExpiredMeetings) {
checkAndRemoveExpiredMeetings();
} else if (message instanceof CreateMeeting) {
processCreateMeeting((CreateMeeting) message);
} else if (message instanceof EndMeeting) {
processEndMeeting((EndMeeting) message);
} else if (message instanceof RegisterUser) {
processRegisterUser((RegisterUser) message);
} else if (message instanceof StunTurnInfoRequested) {
processStunTurnInfoRequested((StunTurnInfoRequested) message);
} else if (message instanceof CreateBreakoutRoom) {
processCreateBreakoutRoom((CreateBreakoutRoom) message);
} else if (message instanceof EndBreakoutRoom) {
processEndBreakoutRoom((EndBreakoutRoom) message);
}
}
};
runExec.execute(task);
}
public void start() {
log.info("Starting Meeting Service.");
try {
processMessage = true;
Runnable messageReceiver = new Runnable() {
public void run() {
while (processMessage) {
try {
IMessage msg = receivedMessages.take();
processMessage(msg);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
log.error("Handling unexpected exception [{}]",
e.toString());
}
}
}
};
msgProcessorExec.execute(messageReceiver);
} catch (Exception e) {
log.error("Error PRocessing Message");
}
}
public void stop() {
processMessage = false;
cleaner.stop();
registeredUserCleaner.stop();
}
public void setDefaultMeetingCreateJoinDuration(int expiration) {
this.defaultMeetingCreateJoinDuration = expiration;
}
public void setDefaultMeetingExpireDuration(int meetingExpiration) {
this.defaultMeetingExpireDuration = meetingExpiration;
}
public void setRecordingService(RecordingService s) {
recordingService = s;
}
public void setMessagingService(MessagingService mess) {
messagingService = mess;
}
public void setExpiredMeetingCleanupTimerTask(
ExpiredMeetingCleanupTimerTask c) {
cleaner = c;
cleaner.setMeetingService(this);
cleaner.start();
}
public void setRemoveMeetingWhenEnded(boolean s) {
removeMeetingWhenEnded = s;
}
public void setRegisteredUserCleanupTimerTask(
RegisteredUserCleanupTimerTask c) {
registeredUserCleaner = c;
registeredUserCleaner.setMeetingService(this);
registeredUserCleaner.start();
}
public void setStunTurnService(StunTurnService s) {
stunTurnService = s;
}
}