/** * 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; }
/** * 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()); } } }
/** * Gets a <tt>Content</tt> of this <tt>Conference</tt> which has a specific name. If a * <tt>Content</tt> of this <tt>Conference</tt> with the specified <tt>name</tt> does not exist at * the time the method is invoked, the method initializes a new <tt>Content</tt> instance with the * specified <tt>name</tt> and adds it to the list of <tt>Content</tt>s of this * <tt>Conference</tt>. * * @param name the name of the <tt>Content</tt> which is to be returned * @return a <tt>Content</tt> of this <tt>Conference</tt> which has the specified <tt>name</tt> */ public Content getOrCreateContent(String name) { Content content; synchronized (contents) { for (Content aContent : contents) { if (aContent.getName().equals(name)) { aContent.touch(); // It seems the content is still active. return aContent; } } content = new Content(this, name); if (isRecording()) { content.setRecording(true, getRecordingPath()); } contents.add(content); } if (logger.isInfoEnabled()) { /* * The method Videobridge.getChannelCount() should better be * executed outside synchronized blocks in order to reduce the risks * of causing deadlocks. */ Videobridge videobridge = getVideobridge(); logger.info( "Created content " + name + " of conference " + getID() + ". " + videobridge.getConferenceCountString()); } return content; }
/** * 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)); }
/** * Tests when requesting a raw UDP channel, we do not get candidates with 127.0.0.1 IPs. * * <p>Test fails only if the machine has only a loopback interface, which is very unlikely. */ @Test public void testNo_127_0_0_0_CandidateIps() throws Exception { String focusJid = "focusJid"; ColibriConferenceIQ confIq = ColibriUtilities.createConferenceIq(focusJid); confIq.getContents().get(0).getChannel(0).setTransport(new RawUdpTransportPacketExtension()); IQ respIq = bridge.handleColibriConferenceIQ(confIq); assertTrue(respIq instanceof ColibriConferenceIQ); ColibriConferenceIQ respConfIq = (ColibriConferenceIQ) respIq; for (CandidatePacketExtension candidate : respConfIq.getContents().get(0).getChannel(0).getTransport().getCandidateList()) { assertNotEquals("127.0.0.1", candidate.getAttribute("ip")); } }
/** Tests requesting a raw UDP channel with RTP level relay type MIXER */ @Test public void testMixerChannel() throws Exception { String focusJid = "focusJid"; ColibriConferenceIQ confIq = ColibriUtilities.createConferenceIq(focusJid); ColibriConferenceIQ.Channel channel = confIq.getContents().get(0).getChannel(0); channel.setTransport(new RawUdpTransportPacketExtension()); channel.setRTPLevelRelayType(RTPLevelRelayType.MIXER); IQ respIq = bridge.handleColibriConferenceIQ(confIq); assertTrue(respIq instanceof ColibriConferenceIQ); ColibriConferenceIQ respConfIq = (ColibriConferenceIQ) respIq; assertEquals( RTPLevelRelayType.MIXER, respConfIq.getContents().get(0).getChannel(0).getRTPLevelRelayType()); }