/** * Gets an <tt>Endpoint</tt> participating in this <tt>Conference</tt> which has a specific * identifier/ID. If an <tt>Endpoint</tt> participating in this <tt>Conference</tt> with the * specified <tt>id</tt> does not exist at the time the method is invoked, the method optionally * initializes a new <tt>Endpoint</tt> instance with the specified <tt>id</tt> and adds it to the * list of <tt>Endpoint</tt>s participating in this <tt>Conference</tt>. * * @param id the identifier/ID of the <tt>Endpoint</tt> which is to be returned * @return an <tt>Endpoint</tt> participating in this <tt>Conference</tt> which has the specified * <tt>id</tt> or <tt>null</tt> if there is no such <tt>Endpoint</tt> and <tt>create</tt> * equals <tt>false</tt> */ private Endpoint getEndpoint(String id, boolean create) { Endpoint endpoint = null; boolean changed = false; synchronized (endpoints) { for (Iterator<WeakReference<Endpoint>> i = endpoints.iterator(); i.hasNext(); ) { Endpoint e = i.next().get(); if (e == null) { i.remove(); changed = true; } else if (e.getID().equals(id)) { endpoint = e; } } if (create && endpoint == null) { endpoint = new Endpoint(id, this); // The propertyChangeListener will weakly reference this // Conference and will unregister itself from the endpoint // sooner or later. endpoint.addPropertyChangeListener(propertyChangeListener); endpoints.add(new WeakReference<>(endpoint)); changed = true; EventAdmin eventAdmin = videobridge.getEventAdmin(); if (eventAdmin != null) eventAdmin.sendEvent(EventFactory.endpointCreated(endpoint)); } } if (changed) firePropertyChange(ENDPOINTS_PROPERTY_NAME, null, null); return endpoint; }
/** * Initializes a new <tt>Conference</tt> instance which is to represent a conference in the terms * of Jitsi Videobridge which has a specific (unique) ID and is managed by a conference focus with * a specific JID. * * @param videobridge the <tt>Videobridge</tt> on which the new <tt>Conference</tt> instance is to * be initialized * @param id the (unique) ID of the new instance to be initialized * @param focus the JID of the conference focus who has requested the initialization of the new * instance and from whom further/future requests to manage the new instance must come or they * will be ignored. Pass <tt>null</tt> to override this safety check. */ public Conference(Videobridge videobridge, String id, String focus) { if (videobridge == null) throw new NullPointerException("videobridge"); if (id == null) throw new NullPointerException("id"); this.videobridge = videobridge; this.id = id; this.focus = focus; this.lastKnownFocus = focus; speechActivity = new ConferenceSpeechActivity(this); speechActivity.addPropertyChangeListener(propertyChangeListener); EventAdmin eventAdmin = videobridge.getEventAdmin(); if (eventAdmin != null) eventAdmin.sendEvent(EventFactory.conferenceCreated(this)); }
/** * Expires this <tt>Conference</tt>, its <tt>Content</tt>s and their respective <tt>Channel</tt>s. * Releases the resources acquired by this instance throughout its life time and prepares it to be * garbage collected. */ public void expire() { synchronized (this) { if (expired) return; else expired = true; } EventAdmin eventAdmin = videobridge.getEventAdmin(); if (eventAdmin != null) eventAdmin.sendEvent(EventFactory.conferenceExpired(this)); setRecording(false); if (recorderEventHandler != null) { recorderEventHandler.close(); recorderEventHandler = null; } Videobridge videobridge = getVideobridge(); try { videobridge.expireConference(this); } finally { // Expire the Contents of this Conference. for (Content content : getContents()) { try { content.expire(); } catch (Throwable t) { logger.warn( "Failed to expire content " + content.getName() + " of conference " + getID() + "!", t); if (t instanceof InterruptedException) Thread.currentThread().interrupt(); else if (t instanceof ThreadDeath) throw (ThreadDeath) t; } } // Close the transportManagers of this Conference. Normally, there // will be no TransportManager left to close at this point because // all Channels have expired and the last Channel to be removed from // a TransportManager closes the TransportManager. However, a // Channel may have expired before it has learned of its // TransportManager and then the TransportManager will not close. closeTransportManagers(); if (logger.isInfoEnabled()) { logger.info( "Expired conference " + getID() + ". " + videobridge.getConferenceCountString()); } } }
/** * Makes sure that conference is allocated for given <tt>room</tt>. * * @param room name of the MUC room of Jitsi Meet conference. * @param properties configuration properties, see {@link JitsiMeetConfig} for the list of valid * properties. * @throws Exception if any error occurs. */ private void createConference(String room, Map<String, String> properties) throws Exception { JitsiMeetConfig config = new JitsiMeetConfig(properties); JitsiMeetConference conference = new JitsiMeetConference(room, focusUserName, protocolProviderHandler, this, config); conferences.put(room, conference); StringBuilder options = new StringBuilder(); for (Map.Entry<String, String> option : properties.entrySet()) { options.append("\n ").append(option.getKey()).append(": ").append(option.getValue()); } logger.info( "Created new focus for " + room + "@" + focusUserDomain + " conferences count: " + conferences.size() + " options:" + options.toString()); // Send focus created event EventAdmin eventAdmin = FocusBundleActivator.getEventAdmin(); if (eventAdmin != null) { eventAdmin.sendEvent(EventFactory.focusCreated(conference.getId(), conference.getRoomName())); } try { conference.start(); } catch (Exception e) { logger.info("Exception while trying to start the conference", e); // stop() method is called by the conference automatically in order // to not release the lock on JitsiMeetConference instance and avoid // a deadlock. It may happen when this thread is about to call // conference.stop() and another thread has entered the method // before us. That other thread will try to call // FocusManager.conferenceEnded, but we're still holding the lock // on FocusManager instance. // conference.stop(); throw e; } }
/** * Makes sure that conference is allocated for given <tt>room</tt>. * * @param room name of the MUC room of Jitsi Meet conference. * @param properties configuration properties, see {@link JitsiMeetConfig} for the list of valid * properties. * @throws Exception if any error occurs. */ private void createConference(String room, Map<String, String> properties) throws Exception { JitsiMeetConfig config = new JitsiMeetConfig(properties); JitsiMeetConference conference = new JitsiMeetConference(room, focusUserName, protocolProviderHandler, this, config); conferences.put(room, conference); StringBuilder options = new StringBuilder(); for (Map.Entry<String, String> option : properties.entrySet()) { options.append("\n ").append(option.getKey()).append(": ").append(option.getValue()); } logger.info( "Created new focus for " + room + "@" + focusUserDomain + " conferences count: " + conferences.size() + " options:" + options.toString()); // Send focus created event EventAdmin eventAdmin = FocusBundleActivator.getEventAdmin(); if (eventAdmin != null) { eventAdmin.sendEvent(EventFactory.focusCreated(conference.getId(), conference.getRoomName())); } try { conference.start(); } catch (Exception e) { logger.info("Exception while trying to start the conference", e); conference.stop(); throw e; } }
/** Handles XEP-0337 "log" extensions. */ private void handleLogRequest(LogPacketExtension log, String jid) { JitsiMeetConference conference = getConferenceForMucJid(jid); if (conference == null) { logger.debug("Room not found for JID: " + jid); return; } Participant participant = conference.findParticipantForRoomJid(jid); if (participant == null) { logger.info("Ignoring log request from an unknown JID: " + jid); return; } EventAdmin eventAdmin = FocusBundleActivator.getEventAdmin(); if (eventAdmin == null) return; if (LogUtil.LOG_ID_PC_STATS.equals(log.getID())) { String content = LogUtil.getContent(log); if (content != null) { ColibriConference colibriConference = conference.getColibriConference(); if (colibriConference != null) { Event event = EventFactory.peerConnectionStats( colibriConference.getConferenceId(), participant.getEndpointId(), content); if (event != null) eventAdmin.sendEvent(event); } else { logger.warn("Unhandled log request" + " - no valid Colibri conference"); } } } else if (logger.isInfoEnabled()) { logger.info("Ignoring log request with an unknown ID:" + log.getID()); } }
/** {@inheritDoc} */ @Override public synchronized void conferenceEnded(JitsiMeetConference conference) { String roomName = conference.getRoomName(); conferences.remove(roomName); logger.info( "Disposed conference for room: " + roomName + " conference count: " + conferences.size()); if (focusAllocListener != null) { focusAllocListener.onFocusDestroyed(roomName); } // Send focus destroyed event EventAdmin eventAdmin = FocusBundleActivator.getEventAdmin(); if (eventAdmin != null) { eventAdmin.sendEvent( EventFactory.focusDestroyed(conference.getId(), conference.getRoomName())); } maybeDoShutdown(); }
/** * Updates an <tt>Endpoint</tt> of this <tt>Conference</tt> with the information contained in * <tt>colibriEndpoint</tt>. The ID of <tt>colibriEndpoint</tt> is used to select the * <tt>Endpoint</tt> to update. * * @param colibriEndpoint a <tt>ColibriConferenceIQ.Endpoint</tt> instance that contains * information to be set on an <tt>Endpoint</tt> instance of this <tt>Conference</tt>. */ void updateEndpoint(ColibriConferenceIQ.Endpoint colibriEndpoint) { String id = colibriEndpoint.getId(); if (id != null) { Endpoint endpoint = getEndpoint(id); if (endpoint != null) { String oldDisplayName = endpoint.getDisplayName(); String newDisplayName = colibriEndpoint.getDisplayName(); if ((oldDisplayName == null && newDisplayName != null) || (oldDisplayName != null && !oldDisplayName.equals(newDisplayName))) { endpoint.setDisplayName(newDisplayName); if (isRecording() && endpointRecorder != null) endpointRecorder.updateEndpoint(endpoint); EventAdmin eventAdmin = getVideobridge().getEventAdmin(); if (eventAdmin != null) { eventAdmin.sendEvent(EventFactory.endpointDisplayNameChanged(endpoint)); } } } } }
/** * Handles presence stanzas * * @param presence */ private void handlePresence(Presence presence) { // unavailable is sent when user leaves the room if (!presence.isAvailable()) { return; } String from = presence.getFrom(); JitsiMeetConference conference = getConferenceForMucJid(from); if (conference == null) { if (logger.isDebugEnabled()) { logger.debug("Room not found for JID: " + from); } return; } ChatRoomMemberRole role = conference.getRoleForMucJid(from); if (role != null && role.compareTo(ChatRoomMemberRole.MODERATOR) < 0) { StartMutedPacketExtension ext = (StartMutedPacketExtension) presence.getExtension( StartMutedPacketExtension.ELEMENT_NAME, StartMutedPacketExtension.NAMESPACE); if (ext != null) { boolean[] startMuted = {ext.getAudioMuted(), ext.getVideoMuted()}; conference.setStartMuted(startMuted); } } Participant participant = conference.findParticipantForRoomJid(from); ColibriConference colibriConference = conference.getColibriConference(); if (participant != null && colibriConference != null) { // Check if this conference is valid String conferenceId = colibriConference.getConferenceId(); if (StringUtils.isNullOrEmpty(conferenceId)) { logger.error("Unable to send DisplayNameChanged event" + " - no conference id"); return; } // Check for changes to the display name String oldDisplayName = participant.getDisplayName(); String newDisplayName = null; for (PacketExtension pe : presence.getExtensions()) { if (pe instanceof Nick) { newDisplayName = ((Nick) pe).getName(); break; } } if (!Objects.equals(oldDisplayName, newDisplayName)) { participant.setDisplayName(newDisplayName); EventAdmin eventAdmin = FocusBundleActivator.getEventAdmin(); if (eventAdmin != null) { // Prevent NPE when adding to event hashtable if (newDisplayName == null) { newDisplayName = ""; } eventAdmin.sendEvent( EventFactory.endpointDisplayNameChanged( conferenceId, participant.getEndpointId(), newDisplayName)); } } } }