/** * Allocates new focus for given MUC room. * * @param room the name of MUC room for which new conference has to be allocated. * @param properties configuration properties map included in the request. * @return <tt>true</tt> if conference focus is in the room and ready to handle session * participants. * @throws Exception if for any reason we have failed to create the conference */ public synchronized boolean conferenceRequest(String room, Map<String, String> properties) throws Exception { if (StringUtils.isNullOrEmpty(room)) return false; room = room.toLowerCase(); if (!conferences.containsKey(room)) { if (shutdownInProgress) return false; createConference(room, properties); } JitsiMeetConference conference = conferences.get(room); return conference.isInTheRoom(); }
/** * Closes given {@link #transportManagers} of this <tt>Conference</tt> and removes corresponding * channel bundle. */ void closeTransportManager(TransportManager transportManager) { synchronized (transportManagers) { for (Iterator<IceUdpTransportManager> i = transportManagers.values().iterator(); i.hasNext(); ) { if (i.next() == transportManager) { i.remove(); // Presumably, we have a single association for // transportManager. break; } } // Close manager try { transportManager.close(); } catch (Throwable t) { logger.warn( "Failed to close an IceUdpTransportManager of" + " conference " + getID() + "!", t); // The whole point of explicitly closing the // transportManagers of this Conference is to prevent memory // leaks. Hence, it does not make sense to possibly leave // TransportManagers open because a TransportManager has // failed to close. if (t instanceof InterruptedException) Thread.currentThread().interrupt(); else if (t instanceof ThreadDeath) throw (ThreadDeath) t; } } }
/** * 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; } }
private void maybeDoShutdown() { if (shutdownInProgress && conferences.isEmpty()) { logger.info("Focus is shutting down NOW"); ShutdownService shutdownService = ServiceUtils.getService(FocusBundleActivator.bundleContext, ShutdownService.class); shutdownService.beginShutdown(); } }
/** * Returns {@link JitsiMeetConference} for given MUC {@code roomName} or {@code null} if no * conference has been allocated yet. * * @param roomName the name of MUC room for which we want get the {@code JitsiMeetConference} * instance. * @return the {@code JitsiMeetConference} for the specified {@code roomName} or {@code null} if * no conference has been allocated yet */ public JitsiMeetConference getConference(String roomName) { roomName = roomName.toLowerCase(); // Other public methods which read from and/or write to the field // conferences are sychronized (e.g. conferenceEnded, conferenceRequest) // so synchronization is necessary here as well. synchronized (this) { return conferences.get(roomName); } }
/** * Returns, the <tt>TransportManager</tt> instance for the channel-bundle with ID * <tt>channelBundleId</tt>. If no instance exists and <tt>create</tt> is <tt>true</tt>, one will * be created. * * @param channelBundleId the ID of the channel-bundle for which to return the * <tt>TransportManager</tt>. * @param create whether to create a new instance, if one doesn't exist. * @return the <tt>TransportManager</tt> instance for the channel-bundle with ID * <tt>channelBundleId</tt>. */ IceUdpTransportManager getTransportManager(String channelBundleId, boolean create) { IceUdpTransportManager transportManager; synchronized (transportManagers) { transportManager = transportManagers.get(channelBundleId); if (transportManager == null && create && !isExpired()) { try { // FIXME: the initiator is hard-coded // We assume rtcp-mux when bundle is used, so we make only // one component. transportManager = new IceUdpTransportManager(this, true, 1); } catch (IOException ioe) { throw new UndeclaredThrowableException(ioe); } transportManagers.put(channelBundleId, transportManager); } } return transportManager; }
/** * Adds the channel-bundles of this <tt>Conference</tt> as * <tt>ColibriConferenceIQ.ChannelBundle</tt> instances in <tt>iq</tt>. * * @param iq the <tt>ColibriConferenceIQ</tt> in which to describe. */ void describeChannelBundles(ColibriConferenceIQ iq) { synchronized (transportManagers) { for (Map.Entry<String, IceUdpTransportManager> entry : transportManagers.entrySet()) { ColibriConferenceIQ.ChannelBundle responseBundleIQ = new ColibriConferenceIQ.ChannelBundle(entry.getKey()); entry.getValue().describe(responseBundleIQ); iq.addChannelBundle(responseBundleIQ); } } }
/** Closes the {@link #transportManagers} of this <tt>Conference</tt>. */ private void closeTransportManagers() { synchronized (transportManagers) { for (Iterator<IceUdpTransportManager> i = transportManagers.values().iterator(); i.hasNext(); ) { IceUdpTransportManager transportManager = i.next(); i.remove(); closeTransportManager(transportManager); } } }
/** {@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(); }
/** Returns the number of currently allocated focus instances. */ public int getConferenceCount() { return conferences.size(); }