/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.protocol.jabber;
import java.util.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.media.*;
import net.java.sip.communicator.util.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.*;
import org.jivesoftware.smack.packet.*;
/**
* Implements OperationSetVideoBridge for Jabber.
*
* @author Yana Stamcheva
* @author Lyubomir Marinov
*/
public class OperationSetVideoBridgeImpl
implements OperationSetVideoBridge,
PacketFilter,
PacketListener,
RegistrationStateChangeListener
{
/**
* The Logger used by the OperationSetVideoBridgeImpl
* class and its instances for logging output.
*/
private static final Logger logger
= Logger.getLogger(OperationSetVideoBridgeImpl.class);
/**
* The ProtocolProviderService implementation which initialized
* this instance, owns it and is often referred to as its parent.
*/
private final ProtocolProviderServiceJabberImpl protocolProvider;
/**
* Creates an instance of OperationSetVideoBridgeImpl by
* specifying the parent ProtocolProviderService announcing this
* operation set.
*
* @param protocolProvider the parent Jabber protocol provider
*/
public OperationSetVideoBridgeImpl(
ProtocolProviderServiceJabberImpl protocolProvider)
{
this.protocolProvider = protocolProvider;
this.protocolProvider.addRegistrationStateChangeListener(this);
}
/**
* Implements {@link PacketFilter}. Determines whether this instance is
* interested in a specific {@link Packet}.
* OperationSetVideoBridgeImpl returns true if the
* specified packet is a {@link ColibriConferenceIQ}; otherwise,
* false.
*
* @param packet the Packet to be determined whether this instance
* is interested in it
* @return true if the specified packet is a
* ColibriConferenceIQ; otherwise, false
*/
public boolean accept(Packet packet)
{
return (packet instanceof ColibriConferenceIQ);
}
/**
* Creates a conference call with the specified callees as call peers via a
* video bridge provided by the parent Jabber provider.
*
* @param callees the list of addresses that we should call
* @return the newly created conference call containing all CallPeers
* @throws OperationFailedException if establishing the conference call
* fails
* @throws OperationNotSupportedException if the provider does not have any
* conferencing features.
*/
public Call createConfCall(String[] callees)
throws OperationFailedException,
OperationNotSupportedException
{
return
protocolProvider
.getOperationSet(OperationSetTelephonyConferencing.class)
.createConfCall(
callees,
new MediaAwareCallConference(true));
}
/**
* Invites the callee represented by the specified uri to an already
* existing call using a video bridge provided by the parent Jabber provider.
* The difference between this method and createConfCall is that
* inviteCalleeToCall allows a user to add new peers to an already
* established conference.
*
* @param uri the callee to invite to an existing conf call.
* @param call the call that we should invite the callee to.
* @return the CallPeer object corresponding to the callee represented by
* the specified uri.
* @throws OperationFailedException if inviting the specified callee to the
* specified call fails
* @throws OperationNotSupportedException if allowing additional callees to
* a pre-established call is not supported.
*/
public CallPeer inviteCalleeToCall(String uri, Call call)
throws OperationFailedException,
OperationNotSupportedException
{
return
protocolProvider
.getOperationSet(OperationSetTelephonyConferencing.class)
.inviteCalleeToCall(uri, call);
}
/**
* Indicates if there's an active video bridge available at this moment. The
* Jabber provider may announce support for video bridge, but it should not
* be used for calling until it becomes actually active.
*
* @return true to indicate that there's currently an active
* available video bridge, false - otherwise
*/
public boolean isActive()
{
String jitsiVideobridge = protocolProvider.getJitsiVideobridge();
return ((jitsiVideobridge != null) && (jitsiVideobridge.length() > 0));
}
/**
* Notifies this instance that a specific ColibriConferenceIQ has
* been received.
*
* @param conferenceIQ the ColibriConferenceIQ which has been
* received
*/
private void processColibriConferenceIQ(ColibriConferenceIQ conferenceIQ)
{
/*
* The application is not a Jitsi Videobridge server, it is a client.
* Consequently, the specified ColibriConferenceIQ is sent to it in
* relation to the part of the application's functionality which makes
* requests to a Jitsi Videobridge server i.e. CallJabberImpl.
*
* Additionally, the method processColibriConferenceIQ is presently tasked
* with processing ColibriConferenceIQ requests only. They are SET IQs
* sent by the Jitsi Videobridge server to notify the application about
* updates in the states of (colibri) conferences organized by the
* application.
*/
if (IQ.Type.SET.equals(conferenceIQ.getType())
&& conferenceIQ.getID() != null)
{
OperationSetBasicTelephony> basicTelephony
= protocolProvider.getOperationSet(
OperationSetBasicTelephony.class);
if (basicTelephony != null)
{
Iterator extends Call> i = basicTelephony.getActiveCalls();
while (i.hasNext())
{
Call call = i.next();
if (call instanceof CallJabberImpl)
{
CallJabberImpl callJabberImpl = (CallJabberImpl) call;
MediaAwareCallConference conference
= callJabberImpl.getConference();
if ((conference != null)
&& conference.isJitsiVideobridge())
{
/*
* TODO We may want to disallow rogue CallJabberImpl
* instances which may throw an exception to prevent
* the conferenceIQ from reaching the CallJabberImpl
* instance which it was meant for.
*/
if (callJabberImpl.processColibriConferenceIQ(
conferenceIQ))
break;
}
}
}
}
}
}
/**
* Implements {@link PacketListener}. Notifies this instance that a specific
* {@link Packet} (which this instance has already expressed interest into
* by returning true from {@link #accept(Packet)}) has been
* received.
*
* @param packet the Packet which has been received and which this
* instance is given a chance to process
*/
public void processPacket(Packet packet)
{
/*
* As we do elsewhere, acknowledge the receipt of the Packet first and
* then go about our business with it.
*/
IQ iq = (IQ) packet;
if (iq.getType() == IQ.Type.SET)
protocolProvider.getConnection().sendPacket(IQ.createResultIQ(iq));
/*
* Now that the acknowledging is out of the way, do go about our
* business with the Packet.
*/
ColibriConferenceIQ conferenceIQ = (ColibriConferenceIQ) iq;
boolean interrupted = false;
try
{
processColibriConferenceIQ(conferenceIQ);
}
catch (Throwable t)
{
logger.error(
"An error occurred during the processing of a "
+ packet.getClass().getName() + " packet",
t);
if (t instanceof InterruptedException)
{
/*
* We cleared the interrupted state of the current Thread by
* catching the InterruptedException. However, we do not really
* care whether the current Thread has been interrupted - we
* caught the InterruptedException because we want to swallow
* any Throwable. Consequently, we should better restore the
* interrupted state.
*/
interrupted = true;
}
else if (t instanceof ThreadDeath)
throw (ThreadDeath) t;
}
if (interrupted)
Thread.currentThread().interrupt();
}
/**
* {@inheritDoc}
*
* Implements {@link RegistrationStateChangeListener}. Notifies this
* instance that there has been a change in the RegistrationState
* of {@link #protocolProvider}. Subscribes this instance to
* {@link ColibriConferenceIQ}s as soon as protocolProvider is
* registered and unsubscribes it as soon as protocolProvider is
* unregistered.
*/
public void registrationStateChanged(RegistrationStateChangeEvent ev)
{
RegistrationState registrationState = ev.getNewState();
if (RegistrationState.REGISTERED.equals(registrationState))
{
protocolProvider.getConnection().addPacketListener(this, this);
}
else if (RegistrationState.UNREGISTERED.equals(registrationState))
{
XMPPConnection connection = protocolProvider.getConnection();
if (connection != null)
connection.removePacketListener(this);
}
}
}