ProtocolProviderService implementation which created
+ * The Logger used by the
+ * OperationSetTelephonyConferencingSipImpl class and its instances
+ * for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(OperationSetTelephonyConferencingSipImpl.class);
+
+ /**
+ * The content sub-type of the content supported in NOTIFY requests handled
+ * by OperationSetTelephonyConferencingSipImpl.
+ */
+ private static final String CONTENT_SUB_TYPE = "conference-info+xml";
+
+ /**
+ * The name of the conference-info XML element
+ * conference-description.
+ */
+ private static final String ELEMENT_CONFERENCE_DESCRIPTION
+ = "conference-description";
+
+ /**
+ * The name of the conference-info XML element conference-info.
+ */
+ private static final String ELEMENT_CONFERENCE_INFO = "conference-info";
+
+ /**
+ * The name of the conference-info XML element conference-state.
+ */
+ private static final String ELEMENT_CONFERENCE_STATE = "conference-state";
+
+ /**
+ * The name of the conference-info XML element display-text.
+ */
+ private static final String ELEMENT_DISPLAY_TEXT = "display-text";
+
+ /**
+ * The name of the conference-info XML element endpoint.
+ */
+ private static final String ELEMENT_ENDPOINT = "endpoint";
+
+ /**
+ * The name of the conference-info XML element media.
+ */
+ private static final String ELEMENT_MEDIA = "media";
+
+ /**
+ * The name of the conference-info XML element src-id.
+ */
+ private static final String ELEMENT_SRC_ID = "src-id";
+
+ /**
+ * The name of the conference-info XML element status.
+ */
+ private static final String ELEMENT_STATUS = "status";
+
+ /**
+ * The name of the conference-info XML element type.
+ */
+ private static final String ELEMENT_TYPE = "type";
+
+ /**
+ * The name of the conference-info XML element user.
+ */
+ private static final String ELEMENT_USER = "user";
+
+ /**
+ * The name of the conference-info XML element user-count.
+ */
+ private static final String ELEMENT_USER_COUNT = "user-count";
+
+ /**
+ * The name of the conference-info XML element users.
+ */
+ private static final String ELEMENT_USERS = "users";
+
+ /**
+ * The name of the event package supported by
+ * OperationSetTelephonyConferencingSipImpl in SUBSCRIBE and NOTIFY
+ * requests.
+ */
+ private static final String EVENT_PACKAGE = "conference";
+
+ /**
+ * The time in seconds before the expiration of a Subscription at
+ * which the OperationSetTelephonyConferencingSipImpl instance
+ * managing it should refresh it.
+ */
+ private static final int REFRESH_MARGIN = 60;
+
+ /**
+ * The time in seconds after which a Subscription should be expired
+ * by the OperationSetTelephonyConferencingSipImpl instance which
+ * manages it.
+ */
+ private static final int SUBSCRIPTION_DURATION = 3600;
+
+ /**
+ * The SIP OperationSetBasicTelephony implementation which this
+ * instance uses to carry out tasks such as establishing Calls.
+ */
+ private OperationSetBasicTelephonySipImpl basicTelephony;
+
+ /**
+ * The utility which encodes text so that it's acceptable as the text of an
+ * XML element or attribute.
+ */
+ private DOMElementWriter domElementWriter = new DOMElementWriter();
+
+ /**
+ * The EventPackageNotifier which implements conference
+ * event-package notifier support on behalf of this
+ * OperationSetTelephonyConferencing instance.
+ */
+ private final EventPackageNotifier notifier;
+
+ /**
+ * The SIP ProtocolProviderService implementation which created
* this instance and for which telephony conferencing services are being
* provided by this instance.
*/
private final ProtocolProviderServiceSipImpl parentProvider;
/**
- * Initializes a new OperationSetTelephonyConferencingSipImpl
- * instance which is to provide telephony conferencing services for a
- * specific SIP ProtocolProviderService implementation.
+ * The EventPackageNotifier which implements conference
+ * event-package subscriber support on behalf of this
+ * OperationSetTelephonyConferencing instance.
+ */
+ private final EventPackageSubscriber subscriber;
+
+ /**
+ * The Timer which executes delayed tasks scheduled by
+ * {@link #notifier} and {@link #subscriber}.
+ */
+ private final TimerScheduler timer = new TimerScheduler();
+
+ /**
+ * Initializes a new OperationSetTelephonyConferencingSipImpl
+ * instance which is to provide telephony conferencing services for the
+ * specified SIP ProtocolProviderService implementation.
*
- * @param parentProvider
- * the SIP ProtocolProviderService which has
- * requested the creation of the new instance and for which the
- * new instance is to provide telephony conferencing services
+ * @param parentProvider the SIP ProtocolProviderService
+ * implementation which has requested the creation of the new instance and
+ * for which the new instance is to provide telephony conferencing services
*/
public OperationSetTelephonyConferencingSipImpl(
ProtocolProviderServiceSipImpl parentProvider)
{
this.parentProvider = parentProvider;
+
+ this.subscriber
+ = new EventPackageSubscriber(
+ this.parentProvider,
+ EVENT_PACKAGE,
+ SUBSCRIPTION_DURATION,
+ CONTENT_SUB_TYPE,
+ this.timer,
+ REFRESH_MARGIN);
+ this.notifier
+ = new EventPackageNotifier(
+ this.parentProvider,
+ EVENT_PACKAGE,
+ SUBSCRIPTION_DURATION,
+ CONTENT_SUB_TYPE,
+ this.timer)
+ {
+ protected Subscription createSubscription(
+ Address fromAddress,
+ String eventId)
+ {
+ return
+ new ConferenceNotifierSubscription(
+ fromAddress,
+ eventId);
+ }
+ };
+
+ this.parentProvider.addRegistrationStateChangeListener(this);
}
+ /**
+ * Appends a specific array of Strings to a specific
+ * StringBuffer.
+ *
+ * @param stringBuffer the StringBuffer to append the specified
+ * strings to
+ * @param strings the String values to be appended to the specified
+ * stringBuffer
+ */
+ private static void append(StringBuffer stringBuffer, String... strings)
+ {
+ for (String str : strings)
+ stringBuffer.append(str);
+ }
+
+ /**
+ * Notifies this CallListener that a specific Call has
+ * been established.
+ *
+ * @param event a CallEvent which specified the newly-established
+ * Call
+ */
+ private void callBegun(CallEvent event)
+ {
+ Call call = event.getSourceCall();
+
+ call.addCallChangeListener(this);
+
+ /*
+ * If there were any CallPeers in the Call prior to our realization that
+ * it has begun, pretend that they are added afterwards.
+ */
+ Iterator extends CallPeer> callPeerIter = call.getCallPeers();
+
+ while (callPeerIter.hasNext())
+ callPeerAdded(
+ new CallPeerEvent(
+ callPeerIter.next(),
+ call,
+ CallPeerEvent.CALL_PEER_ADDED));
+ }
+
+ /**
+ * Notifies this CallListener that a specific Call has
+ * ended.
+ *
+ * @param event a CallEvent which specified the Call which
+ * has just ended
+ */
+ public void callEnded(CallEvent event)
+ {
+ Call call = event.getSourceCall();
+
+ /*
+ * If there are still CallPeers after our realization that it has ended,
+ * pretend that they are removed before that.
+ */
+ Iterator extends CallPeer> callPeerIter = call.getCallPeers();
+
+ while (callPeerIter.hasNext())
+ callPeerRemoved(
+ new CallPeerEvent(
+ callPeerIter.next(),
+ call,
+ CallPeerEvent.CALL_PEER_REMOVED));
+
+ call.removeCallChangeListener(this);
+ }
+
+ /**
+ * Notifies this CallChangeListener that a specific
+ * CallPeer has been added to a specific Call.
+ *
+ * @param event a CallPeerEvent which specifies the
+ * CallPeer which has been added to a Call
+ */
+ public void callPeerAdded(CallPeerEvent event)
+ {
+ CallPeerSipImpl callPeer = (CallPeerSipImpl) event.getSourceCallPeer();
+
+ callPeer.addMethodProcessorListener(this);
+
+ callPeersChanged(event);
+ }
+
+ /**
+ * Notifies this CallChangeListener that a specific
+ * CallPeer has been remove from a specific Call.
+ *
+ * @param event a CallPeerEvent which specifies the
+ * CallPeer which has been removed from a Call
+ */
+ public void callPeerRemoved(CallPeerEvent event)
+ {
+ CallPeerSipImpl callPeer = (CallPeerSipImpl) event.getSourceCallPeer();
+
+ callPeer.removeMethodProcessorListener(this);
+
+ callPeersChanged(event);
+ }
+
+ /**
+ * Notifies this CallChangeListener that the CallPeer list
+ * of a specific Call has been modified by adding or removing a
+ * specific CallPeer.
+ *
+ * @param event a CallPeerEvent which specifies the
+ * CallPeer which has been added to or removed from a Call
+ */
+ private void callPeersChanged(CallPeerEvent event)
+ {
+ notifyAll(SubscriptionStateHeader.ACTIVE, null, event.getSourceCall());
+ }
+
+ /**
+ * Notifies this CallChangeListener that a specific Call
+ * has changed its state. Does nothing.
+ *
+ * @param event a CallChangeEvent which specifies the Call
+ * which has changed its state, the very state which has been changed and
+ * the values of the state before and after the change
+ */
+ public void callStateChanged(CallChangeEvent event)
+ {
+ }
+
+ /**
+ * Creates a conference call with the specified callees as call peers.
+ *
+ * @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
+ * @see OperationSetTelephonyConferencing#createConfCall(String[])
+ */
public Call createConfCall(String[] callees)
- throws OperationNotSupportedException
+ throws OperationFailedException
+ {
+ int calleeCount = callees.length;
+ Address[] calleeAddresses = new Address[calleeCount];
+
+ for (int i = 0; i < calleeCount; i++)
+ calleeAddresses[i] = parseAddressString(callees[i]);
+
+ CallSipImpl call = basicTelephony.createOutgoingCall();
+
+ call.setConferenceFocus(true);
+
+ for (Address calleeAddress : calleeAddresses)
+ inviteCalleeToCall(calleeAddress, call);
+ return call;
+ }
+
+ /**
+ * Generates the conference-info XML to be sent to a specific
+ * CallPeer in order to notify it of the current state of the
+ * conference managed by the local peer.
+ *
+ * @param callPeer the CallPeer to generate conference-info XML for
+ * @param version the value of the version attribute of the
+ * conference-info root element of the conference-info XML to be
+ * generated
+ * @return the conference-info XML to be sent to the specified
+ * callPeer in order to notify it of the current state of the
+ * conference managed by the local peer
+ */
+ private String getConferenceInfoXML(CallPeerSipImpl callPeer, int version)
+ {
+ Dialog dialog = callPeer.getDialog();
+ String localParty = null;
+
+ if (dialog != null)
+ {
+ Address localPartyAddress = dialog.getLocalParty();
+
+ if (localPartyAddress != null)
+ localParty = localPartyAddress.getURI().toString();
+ }
+
+ StringBuffer xml = new StringBuffer();
+ CallSipImpl call = callPeer.getCall();
+
+ xml.append( "\r\n");
+ // CallPeer methods with the purpose of only leaving custom
- * protocol development to clients using the PhoneUI service.
+ * Provides a default implementation for most of the CallPeer methods
+ * with the purpose of only leaving custom protocol development to clients using
+ * the PhoneUI service.
*
* @author Emil Ivov
* @author Lubomir Marinov
@@ -32,9 +32,8 @@ public abstract class AbstractCallPeer
= Logger.getLogger(AbstractCallPeer.class);
/**
- * The constant which describes an empty set of
- * ConferenceMembers (and which can be used to reduce
- * allocations).
+ * The constant which describes an empty set of ConferenceMembers
+ * (and which can be used to reduce allocations).
*/
protected static final ConferenceMember[] NO_CONFERENCE_MEMBERS
= new ConferenceMember[0];
@@ -46,8 +45,7 @@ public abstract class AbstractCallPeer
= new ArrayListConferenceMember such as {@link #getConferenceMembers()} and
+ * ConferenceMember such as {@link #getConferenceMembers()} and
* {@link #getConferenceMemberCount()}.
*/
private boolean conferenceFocus;
/**
- * The list of ConferenceMembers currently known to and managed
- * in a conference by this peer.
+ * The list of ConferenceMembers currently known to and managed in
+ * a conference by this peer.
*/
private final ListCallPeerConferenceListeners interested in
- * and to be notified about changes in conference-related information such
- * as this peer acting or not acting as a conference focus and
- * conference membership details.
+ * The list of CallPeerConferenceListeners interested in and to be
+ * notified about changes in conference-related information such as this
+ * peer acting or not acting as a conference focus and conference membership
+ * details.
*/
protected final ListCallPeer transitioned
- * into a state (likely {@link CallPeerState#CONNECTED}) marking the
- * start of the duration of the participation in a Call.
+ * Gets the time at which this CallPeer transitioned into a state
+ * (likely {@link CallPeerState#CONNECTED}) marking the start of the
+ * duration of the participation in a Call.
*
- * @return the time at which this CallPeer transitioned
- * into a state marking the start of the duration of the
- * participation in a Call or
- * {@link CallPeer#CALL_DURATION_START_TIME_UNKNOWN} if such
- * a transition has not been performed
+ * @return the time at which this CallPeer transitioned into a
+ * state marking the start of the duration of the participation in a
+ * Call or {@link CallPeer#CALL_DURATION_START_TIME_UNKNOWN} if
+ * such a transition has not been performed
*/
public long getCallDurationStartTime()
{
@@ -451,14 +450,14 @@ public long getCallDurationStartTime()
}
/**
- * Determines whether the audio stream (if any) being sent to this
- * peer is mute.
+ * Determines whether the audio stream (if any) being sent to this peer is
+ * mute.
* * The default implementation returns false. *
* - * @return true if an audio stream is being sent to this - * peer and it is currently mute; false, otherwise + * @return true if an audio stream is being sent to this peer and + * it is currently mute; false, otherwise */ public boolean isMute() { @@ -508,6 +507,7 @@ public void setConferenceFocus(boolean conferenceFocus) * Implements CallPeer#getConferenceMembers(). In order to reduce * allocations, returns #NO_CONFERENCE_MEMBERS if #conferenceMembers * contains no ConferenceMember instances. + * * @return an array of the conference members */ public ConferenceMember[] getConferenceMembers() @@ -533,6 +533,7 @@ public ConferenceMember[] getConferenceMembers() * Returns the count of the members contained in this peer. *
* Implements CallPeer#getConferenceMemberCount().
+ *
* @return the count of the members contained in this peer
*/
public int getConferenceMemberCount()
@@ -541,19 +542,17 @@ public int getConferenceMemberCount()
}
/**
- * Adds a specific ConferenceMember to the list of
- * ConferenceMembers reported by this peer through
+ * Adds a specific ConferenceMember to the list of
+ * ConferenceMembers reported by this peer through
* {@link #getConferenceMembers()} and {@link #getConferenceMemberCount()}
* and fires
- * CallPeerConferenceEvent#CONFERENCE_MEMBER_ADDED to
- * the currently registered CallPeerConferenceListeners.
+ * CallPeerConferenceEvent#CONFERENCE_MEMBER_ADDED to
+ * the currently registered CallPeerConferenceListeners.
*
- * @param conferenceMember
- * a ConferenceMember to be added to the list of
- * ConferenceMember reported by this peer. If
- * the specified ConferenceMember is already
- * contained in the list, it is not added again and no event is
- * fired.
+ * @param conferenceMember a ConferenceMember to be added to the
+ * list of ConferenceMember reported by this peer. If the specified
+ * ConferenceMember is already contained in the list, it is not
+ * added again and no event is fired.
*/
public void addConferenceMember(ConferenceMember conferenceMember)
{
@@ -573,18 +572,17 @@ public void addConferenceMember(ConferenceMember conferenceMember)
}
/**
- * Removes a specific ConferenceMember from the list of
- * ConferenceMembers reported by this peer through
+ * Removes a specific ConferenceMember from the list of
+ * ConferenceMembers reported by this peer through
* {@link #getConferenceMembers()} and {@link #getConferenceMemberCount()}
* if it is contained and fires
- * CallPeerConferenceEvent#CONFERENCE_MEMBER_REMOVED to
- * the currently registered CallPeerConferenceListeners.
+ * CallPeerConferenceEvent#CONFERENCE_MEMBER_REMOVED to
+ * the currently registered CallPeerConferenceListeners.
*
- * @param conferenceMember
- * a ConferenceMember to be removed from the list of
- * ConferenceMember reported by this peer. If
- * the specified ConferenceMember is no contained in
- * the list, no event is fired.
+ * @param conferenceMember a ConferenceMember to be removed from
+ * the list of ConferenceMember reported by this peer. If the
+ * specified ConferenceMember is no contained in the list, no event
+ * is fired.
*/
public void removeConferenceMember(ConferenceMember conferenceMember)
{
@@ -603,10 +601,12 @@ public void removeConferenceMember(ConferenceMember conferenceMember)
}
/**
- * ImplementsCallPeer#addCallPeerConferenceListener(
- * CallPeerConferenceListener). In the fashion of the addition of the
- * other listeners, does not throw an exception on attempting to add a null
- * listeners and just ignores the call.
+ * Implements
+ * CallPeer#addCallPeerConferenceListener(
+ * CallPeerConferenceListener). In the fashion of the addition of the
+ * other listeners, does not throw an exception on attempting to add a
+ * null listeners and just ignores the call.
+ *
* @param listener the CallPeerConferenceListener to add
*/
public void addCallPeerConferenceListener(
@@ -621,8 +621,10 @@ public void addCallPeerConferenceListener(
}
/**
- * Implements CallPeer#removeCallPeerConferenceListener(
- * CallPeerConferenceListener).
+ * Implements
+ * CallPeer#removeCallPeerConferenceListener(
+ * CallPeerConferenceListener).
+ *
* @param listener the CallPeerConferenceListener to remove
*/
public void removeCallPeerConferenceListener(
@@ -704,13 +706,12 @@ public void removeConferenceMembersSoundLevelListener(
}
/**
- * Fires a specific CallPeerConferenceEvent to the
- * CallPeerConferenceListeners interested in changes in
- * the conference-related information provided by this peer.
+ * Fires a specific CallPeerConferenceEvent to the
+ * CallPeerConferenceListeners interested in changes in the
+ * conference-related information provided by this peer.
*
- * @param conferenceEvent
- * a CallPeerConferenceEvent to be fired and
- * carrying the event data
+ * @param conferenceEvent a CallPeerConferenceEvent to be fired and
+ * carrying the event data
*/
protected void fireCallPeerConferenceEvent(
CallPeerConferenceEvent conferenceEvent)
@@ -728,6 +729,34 @@ protected void fireCallPeerConferenceEvent(
int eventID = conferenceEvent.getEventID();
+ if (logger.isDebugEnabled())
+ {
+ String eventIDString;
+
+ switch (eventID)
+ {
+ case CallPeerConferenceEvent.CONFERENCE_FOCUS_CHANGED:
+ eventIDString = "CONFERENCE_FOCUS_CHANGED";
+ break;
+ case CallPeerConferenceEvent.CONFERENCE_MEMBER_ADDED:
+ eventIDString = "CONFERENCE_MEMBER_ADDED";
+ break;
+ case CallPeerConferenceEvent.CONFERENCE_MEMBER_REMOVED:
+ eventIDString = "CONFERENCE_MEMBER_REMOVED";
+ break;
+ default:
+ eventIDString = "UNKNOWN";
+ break;
+ }
+ logger
+ .debug(
+ "Firing CallPeerConferenceEvent with ID "
+ + eventIDString
+ + " to "
+ + listeners.length
+ + " listeners");
+ }
+
for (CallPeerConferenceListener listener : listeners)
switch (eventID)
{
diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetTelephonyConferencing.java b/src/net/java/sip/communicator/service/protocol/OperationSetTelephonyConferencing.java
index 61a79345c..42cf63aee 100644
--- a/src/net/java/sip/communicator/service/protocol/OperationSetTelephonyConferencing.java
+++ b/src/net/java/sip/communicator/service/protocol/OperationSetTelephonyConferencing.java
@@ -10,40 +10,42 @@
* Provides operations necessary to create and handle conferencing calls.
*
* @author Emil Ivov
+ * @author Lubomir Marinov
*/
public interface OperationSetTelephonyConferencing
extends OperationSet
{
/**
- * Creates a conference call with the specified callees as call
- * peers.
+ * Creates a conference call with the specified callees as call peers.
*
- * @param callees
- * the list of addresses that we should call
+ * @param callees the list of addresses that we should call
* @return the newly created conference call containing all CallPeers
- * @throws OperationNotSupportedException
- * if the provider does not have any conferencing features.
+ * @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 OperationNotSupportedException;
+ throws OperationFailedException,
+ OperationNotSupportedException;
/**
* Invites the callee represented by the specified uri to an already
* existing call. The difference between this method and createConfCall is
* that inviteCalleeToCall allows a user to transform an existing 1 to 1
- * call into a conference call, or add new peers to an already
- * established conference.
+ * call into a conference call, or add new peers to an already established
+ * conference.
*
- * @param uri
- * the callee to invite to an existing conf call.
- * @param existingCall
- * the call that we should invite the callee to.
- * @return the CallPeer object corresponding to the callee
- * represented by the specified uri.
- * @throws OperationNotSupportedException
- * if allowing additional callees to a pre-established call is
- * not supported.
+ * @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 existingCall)
- throws OperationNotSupportedException;
+ public CallPeer inviteCalleeToCall(String uri, Call call)
+ throws OperationFailedException,
+ OperationNotSupportedException;
}