/**
* 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.domain;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.commons.lang3.RandomStringUtils;
public class Meeting {
private static final long MILLIS_IN_A_MINUTE = 60000;
private String name;
private String extMeetingId;
private String intMeetingId;
private String parentMeetingId;
private Integer sequence = 0;
private Integer duration = 0;
private long createdTime = 0;
private long startTime = 0;
private long endTime = 0;
private boolean forciblyEnded = false;
private String telVoice;
private String webVoice;
private String moderatorPass;
private String viewerPass;
private String welcomeMsgTemplate;
private String welcomeMsg;
private String modOnlyMessage;
private String logoutUrl;
private int maxUsers;
private boolean record;
private boolean autoStartRecording = false;
private boolean allowStartStopRecording = false;
private boolean webcamsOnlyForModerator = false;
private String dialNumber;
private String defaultAvatarURL;
private String defaultConfigToken;
private boolean userHasJoined = false;
private Map<String, String> metadata;
private Map<String, Object> userCustomData;
private final ConcurrentMap<String, User> users;
private final ConcurrentMap<String, Long> registeredUsers;
private final ConcurrentMap<String, Config> configs;
private final Boolean isBreakout;
private final List<String> breakoutRooms = new ArrayList();
private long lastUserLeftOn = 0;
public Meeting(Builder builder) {
name = builder.name;
extMeetingId = builder.externalId;
intMeetingId = builder.internalId;
viewerPass = builder.viewerPass;
moderatorPass = builder.moderatorPass;
maxUsers = builder.maxUsers;
logoutUrl = builder.logoutUrl;
defaultAvatarURL = builder.defaultAvatarURL;
record = builder.record;
autoStartRecording = builder.autoStartRecording;
allowStartStopRecording = builder.allowStartStopRecording;
webcamsOnlyForModerator = builder.webcamsOnlyForModerator;
duration = builder.duration;
webVoice = builder.webVoice;
telVoice = builder.telVoice;
welcomeMsgTemplate = builder.welcomeMsgTemplate;
welcomeMsg = builder.welcomeMsg;
dialNumber = builder.dialNumber;
metadata = builder.metadata;
createdTime = builder.createdTime;
isBreakout = builder.isBreakout;
userCustomData = new HashMap<String, Object>();
users = new ConcurrentHashMap<String, User>();
registeredUsers = new ConcurrentHashMap<String, Long>();
configs = new ConcurrentHashMap<String, Config>();
}
public void addBreakoutRoom(String meetingId) {
breakoutRooms.add(meetingId);
}
public List<String> getBreakoutRooms() {
return breakoutRooms;
}
public String storeConfig(boolean defaultConfig, String config) {
String token = RandomStringUtils.randomAlphanumeric(8);
while (configs.containsKey(token)) {
token = RandomStringUtils.randomAlphanumeric(8);
}
configs.put(token, new Config(token, System.currentTimeMillis(), config));
if (defaultConfig) {
defaultConfigToken = token;
}
return token;
}
public Config getDefaultConfig() {
if (defaultConfigToken != null) {
return getConfig(defaultConfigToken);
}
return null;
}
public Config getConfig(String token) {
return configs.get(token);
}
public Config removeConfig(String token) {
return configs.remove(token);
}
public Map<String, String> getMetadata() {
return metadata;
}
public Collection<User> getUsers() {
return users.isEmpty() ? Collections.<User>emptySet() : Collections.unmodifiableCollection(users.values());
}
public ConcurrentMap<String, User> getUsersMap() {
return users;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long t) {
startTime = t;
}
public long getCreateTime() {
return createdTime;
}
public Integer setSequence(Integer s) {
return sequence = s;
}
public Integer getSequence() {
return sequence;
}
public Integer getDuration() {
return duration;
}
public long getEndTime() {
return endTime;
}
public void setModeratorOnlyMessage(String msg) {
modOnlyMessage = msg;
}
public String getModeratorOnlyMessage() {
return modOnlyMessage;
}
public void setEndTime(long t) {
endTime = t;
}
public boolean isRunning() {
return ! users.isEmpty();
}
public Boolean isBreakout() {
return isBreakout;
}
public String getName() {
return name;
}
public boolean isForciblyEnded() {
return forciblyEnded;
}
public void setForciblyEnded(boolean forciblyEnded) {
this.forciblyEnded = forciblyEnded;
}
public String getExternalId() {
return extMeetingId;
}
public String getInternalId() {
return intMeetingId;
}
public String setParentMeetingId(String p) {
return parentMeetingId = p;
}
public String getParentMeetingId() {
return parentMeetingId;
}
public String getWebVoice() {
return webVoice;
}
public String getTelVoice() {
return telVoice;
}
public String getModeratorPassword() {
return moderatorPass;
}
public String getViewerPassword() {
return viewerPass;
}
public String getWelcomeMessageTemplate() {
return welcomeMsgTemplate;
}
public String getWelcomeMessage() {
return welcomeMsg;
}
public String getDefaultAvatarURL() {
return defaultAvatarURL;
}
public String getLogoutUrl() {
return logoutUrl;
}
public int getMaxUsers() {
return maxUsers;
}
public boolean isRecord() {
return record;
}
public boolean getAutoStartRecording() {
return autoStartRecording;
}
public boolean getAllowStartStopRecording() {
return allowStartStopRecording;
}
public boolean getWebcamsOnlyForModerator() {
return webcamsOnlyForModerator;
}
public boolean hasUserJoined() {
return userHasJoined;
}
public void userJoined(User user) {
userHasJoined = true;
this.users.put(user.getInternalUserId(), user);
}
public User userLeft(String userid){
User u = (User) users.remove(userid);
if (users.isEmpty()) lastUserLeftOn = System.currentTimeMillis();
return u;
}
public User getUserById(String id){
return this.users.get(id);
}
public int getNumUsers(){
return this.users.size();
}
public int getNumModerators(){
int sum = 0;
for (String key : users.keySet()) {
User u = (User) users.get(key);
if (u.isModerator()) sum++;
}
return sum;
}
public String getDialNumber() {
return dialNumber;
}
public boolean wasNeverJoined(int expiry) {
return (hasStarted() && !hasEnded() && nobodyJoined(expiry));
}
private boolean meetingInfinite() {
/* Meeting stays runs infinitely */
return duration == 0;
}
private boolean nobodyJoined(int expiry) {
if (expiry == 0) return false; /* Meeting stays created infinitely */
long now = System.currentTimeMillis();
return (!userHasJoined && (now - createdTime) > (expiry * MILLIS_IN_A_MINUTE));
}
private boolean hasBeenEmptyFor(int expiry) {
long now = System.currentTimeMillis();
return (now - lastUserLeftOn > (expiry * MILLIS_IN_A_MINUTE));
}
private boolean isEmpty() {
return users.isEmpty();
}
public boolean hasExpired(int expiry) {
return (hasStarted() && userHasJoined && isEmpty() && hasBeenEmptyFor(expiry));
}
public boolean hasExceededDuration() {
return (hasStarted() && !hasEnded() && pastDuration());
}
private boolean pastDuration() {
if (meetingInfinite()) return false;
long now = System.currentTimeMillis();
return (now - startTime > (duration * MILLIS_IN_A_MINUTE));
}
private boolean hasStarted() {
return startTime > 0;
}
private boolean hasEnded() {
return endTime > 0;
}
public int getNumListenOnly() {
int sum = 0;
for (String key : users.keySet()) {
User u = (User) users.get(key);
if (u.isListeningOnly()) sum++;
}
return sum;
}
public int getNumVoiceJoined() {
int sum = 0;
for (String key : users.keySet()) {
User u = (User) users.get(key);
if (u.isVoiceJoined()) sum++;
}
return sum;
}
public int getNumVideos() {
int sum = 0;
for (String key : users.keySet()) {
User u = (User) users.get(key);
sum += u.getStreams().size();
}
return sum;
}
public void addUserCustomData(String userID, Map<String, String> data) {
userCustomData.put(userID, data);
}
public Map<String, Object> getUserCustomData(String userID){
return (Map<String, Object>) userCustomData.get(userID);
}
/***
* Meeting Builder
*
*/
public static class Builder {
private String name;
private String externalId;
private String internalId;
private int maxUsers;
private boolean record;
private boolean autoStartRecording;
private boolean allowStartStopRecording;
private boolean webcamsOnlyForModerator;
private String moderatorPass;
private String viewerPass;
private int duration;
private String webVoice;
private String telVoice;
private String welcomeMsgTemplate;
private String welcomeMsg;
private String logoutUrl;
private Map<String, String> metadata;
private String dialNumber;
private String defaultAvatarURL;
private long createdTime;
private boolean isBreakout;
public Builder(String externalId, String internalId, long createTime) {
this.externalId = externalId;
this.internalId = internalId;
this.createdTime = createTime;
}
public Builder withName(String name) {
this.name = name;
return this;
}
public Builder withDuration(int minutes) {
duration = minutes;
return this;
}
public Builder withMaxUsers(int n) {
maxUsers = n;
return this;
}
public Builder withRecording(boolean record) {
this.record = record;
return this;
}
public Builder withAutoStartRecording(boolean start) {
this.autoStartRecording = start;
return this;
}
public Builder withAllowStartStopRecording(boolean allow) {
this.allowStartStopRecording = allow;
return this;
}
public Builder withWebcamsOnlyForModerator(boolean only) {
this.webcamsOnlyForModerator = only;
return this;
}
public Builder withWebVoice(String w) {
this.webVoice = w;
return this;
}
public Builder withTelVoice(String t) {
this.telVoice = t;
return this;
}
public Builder withDialNumber(String d) {
this.dialNumber = d;
return this;
}
public Builder withModeratorPass(String p) {
this.moderatorPass = p;
return this;
}
public Builder withViewerPass(String p) {
this.viewerPass = p;
return this;
}
public Builder withWelcomeMessage(String w) {
welcomeMsg = w;
return this;
}
public Builder withWelcomeMessageTemplate(String w) {
welcomeMsgTemplate = w;
return this;
}
public Builder withDefaultAvatarURL(String w) {
defaultAvatarURL = w;
return this;
}
public Builder isBreakout(Boolean b) {
isBreakout = b;
return this;
}
public Builder withLogoutUrl(String l) {
logoutUrl = l;
return this;
}
public Builder withMetadata(Map<String, String> m) {
metadata = m;
return this;
}
public Meeting build() {
return new Meeting(this);
}
}
public void userRegistered(String internalUserID) {
this.registeredUsers.put(internalUserID, new Long(System.nanoTime()));
}
public Long userUnregistered(String userid) {
String internalUserIDSeed = userid.split("_")[0];
Long r = (Long) this.registeredUsers.remove(internalUserIDSeed);
return r;
}
public ConcurrentMap<String, Long> getRegisteredUsers() {
return registeredUsers;
}
}