Attempts to fix the handling of SUBSCRIBE requests which used to cause the conference info to not be received in other than the first conference call.

cusax-fix
Lyubomir Marinov 16 years ago
parent 8052caf691
commit 97634e712a

@ -42,12 +42,6 @@ public abstract class EventPackageNotifier
*/
private static final int SUBSCRIBE_MIN_EXPIRE = 120;
/**
* The list of subscriptions managed by this instance.
*/
private final List<Subscription> subscriptions
= new LinkedList<Subscription>();
/**
* A reference to the <tt>SipMessageFactory</tt> instance that we should
* use when creating requests.
@ -86,35 +80,6 @@ public EventPackageNotifier(
this.messageFactory = protocolProvider.getMessageFactory();
}
/**
* Adds a specific <tt>Subscription</tt> to this list of subscriptions
* managed by this instance. If a <tt>Subscription</tt> with matching
* <tt>Address</tt>/Request URI and EventId tag exists in the list of
* subscriptions managed by this instance already, that matching
* <tt>Subscription</tt> is removed before the specified
* <tt>Subscription</tt> is added.
*
* @param subscription the <tt>Subscription</tt> to be added to the list of
* subscriptions managed by this instance
*/
private void addSubscription(Subscription subscription)
{
synchronized (subscriptions)
{
Address address = subscription.getAddress();
String eventId = subscription.getEventId();
for (Subscription s : subscriptions)
if (s.equals(address, eventId))
{
removeSubscription(s);
break;
}
subscriptions.add(subscription);
}
}
/**
* Creates a NOTIFY request which is to notify about a specific subscription
* state and carry a specific content. This request MUST be sent using
@ -344,32 +309,27 @@ protected abstract Subscription createSubscription(
* if no such <tt>Subscription</tt> exists in the list of subscriptions
* managed by this instance
*/
private Subscription getSubscription(Address fromAddress, String eventId)
@Override
protected Subscription getSubscription(Address fromAddress, String eventId)
{
synchronized (subscriptions)
{
for (Subscription subscription : subscriptions)
if (subscription.equals(fromAddress, eventId))
return subscription;
}
return null;
return (Subscription) super.getSubscription(fromAddress, eventId);
}
/**
* Gets a new copy of the list of <tt>Subscription</tt>s managed by this
* instance.
* Gets the <tt>Subscription</tt> from the list of subscriptions managed by
* this instance which is associated with a specific CallId.
*
* @return a new copy of the list of <tt>Subscription</tt>s managed by this
* instance; if this instance currently manages no <tt>Subscription</tt>s,
* an empty array of <tt>Subscription</tt> element type
* @param callId the CallId associated with the <tt>Subscription</tt> to be
* retrieved
* @return an existing <tt>Subscription</tt> from the list of subscriptions
* managed by this instance which is associated with the specified CallId;
* <tt>null</tt> if no such <tt>Subscription</tt> exists in the list of
* subscriptions managed by this instance
*/
private Subscription[] getSubscriptions()
@Override
protected Subscription getSubscription(String callId)
{
synchronized (subscriptions)
{
return
subscriptions.toArray(new Subscription[subscriptions.size()]);
}
return (Subscription) super.getSubscription(callId);
}
/**
@ -383,8 +343,7 @@ private Subscription[] getSubscriptions()
* @param subscriptionState the subscription state to notify the target
* represented by its <tt>Subscription</tt> about
* @param reason the reason for that subscription state
*
* @throws OperationFailedException
* @throws OperationFailedException if sending the NOTIFY request failed
*/
public void notify( Subscription subscription,
String subscriptionState,
@ -394,6 +353,7 @@ public void notify( Subscription subscription,
Dialog dialog = subscription.getDialog();
ClientTransaction transac
= createNotify(dialog, subscription, subscriptionState, reason);
String callId = dialog.getCallId().getCallId();
try
{
@ -409,7 +369,7 @@ public void notify( Subscription subscription,
}
if (SubscriptionState.TERMINATED.equals(subscriptionState))
removeSubscription(subscription);
removeSubscription(callId, subscription);
}
/**
@ -453,9 +413,13 @@ public void notifyAll(
SubscriptionFilter filter)
throws OperationFailedException
{
for (Subscription subscription : getSubscriptions())
if ((filter == null) || filter.accept(subscription))
notify(subscription, subscriptionState, reason);
for (EventPackageSupport.Subscription subscription : getSubscriptions())
{
Subscription s = (Subscription) subscription;
if ((filter == null) || filter.accept(s))
notify(s, subscriptionState, reason);
}
}
/**
@ -496,6 +460,9 @@ public boolean processRequest(RequestEvent requestEvent)
= (expHeader == null)
? subscriptionDuration
: expHeader.getExpires();
CallIdHeader callIdHeader
= (CallIdHeader) request.getHeader(CallIdHeader.NAME);
String callId = callIdHeader.getCallId();
// interval too brief
if ((expires < SUBSCRIBE_MIN_EXPIRE) && (expires > 0))
@ -516,7 +483,9 @@ public boolean processRequest(RequestEvent requestEvent)
MinExpiresHeader min;
try
{
min = protocolProvider .getHeaderFactory()
min
= protocolProvider
.getHeaderFactory()
.createMinExpiresHeader(SUBSCRIBE_MIN_EXPIRE);
}
catch (InvalidArgumentException e)
@ -540,10 +509,7 @@ public boolean processRequest(RequestEvent requestEvent)
return true;
}
FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
Address fromAddress = fromHeader.getAddress();
String eventId = eventHeader.getEventId();
Subscription subscription = getSubscription(fromAddress, eventId);
Subscription subscription = getSubscription(callId);
/*
* Is it a subscription refresh? (No need to synchronize the access to
@ -622,7 +588,7 @@ public boolean processRequest(RequestEvent requestEvent)
return false;
}
removeSubscription(subscription);
removeSubscription(callId, subscription);
try
{
@ -637,7 +603,14 @@ public boolean processRequest(RequestEvent requestEvent)
}
if (subscription == null)
{
FromHeader fromHeader
= (FromHeader) request.getHeader(FromHeader.NAME);
Address fromAddress = fromHeader.getAddress();
String eventId = eventHeader.getEventId();
subscription = createSubscription(fromAddress, eventId);
}
// Remember the dialog we will use to send the NOTIFYs.
Dialog dialog;
@ -651,7 +624,7 @@ public boolean processRequest(RequestEvent requestEvent)
if (expires == 0)
{
// remove the subscription
removeSubscription(subscription);
removeSubscription(callId, subscription);
// send him OK
Response response;
@ -752,7 +725,7 @@ public boolean processRequest(RequestEvent requestEvent)
return false;
}
addSubscription(subscription);
addSubscription(callId, subscription);
// send a NOTIFY
ClientTransaction transac;
@ -889,10 +862,12 @@ private void removeSubscription(
String eventId,
ClientTransaction clientTransaction)
{
CallIdHeader callIdHeader
= (CallIdHeader) response.getHeader(CallIdHeader.NAME);
String callId = callIdHeader.getCallId();
FromHeader fromHeader
= (FromHeader) response.getHeader(FromHeader.NAME);
Address fromAddress = fromHeader.getAddress();
Subscription subscription = getSubscription(fromAddress, eventId);
Subscription subscription = getSubscription(callId);
if (subscription != null)
{
@ -903,35 +878,13 @@ private void removeSubscription(
*/
synchronized (subscription)
{
if (subscription.getDialog()
.equals(clientTransaction.getDialog()))
removeSubscription(subscription);
if (subscription
.getDialog().equals(clientTransaction.getDialog()))
removeSubscription(callId, subscription);
}
}
}
/**
* Removes a specific <tt>Subscription</tt> from the list of subscriptions
* managed by this instance. If the specified <tt>Subscription</tt> is
* contained in the list of subscriptions managed by this instance, it is
* removed and then its <tt>Subscription#removed()</tt> method is called to
* notify it that it has been removed from the list of subscriptions of its
* containing <tt>EventPackageNotifier</tt>. If the specified
* <tt>Subscription</tt> is not contained in the list of subscriptions
* managed by this instance, does nothing.
*
* @param subscription the <tt>Subscription</tt> to be removed from the list
* of subscriptions managed by this instance
*/
private void removeSubscription(Subscription subscription)
{
synchronized (subscriptions)
{
if (subscriptions.remove(subscription))
subscription.removed();
}
}
/**
* Represents a general event package subscription in the sense of RFC 3265
* "Session Initiation Protocol (SIP)-Specific Event Notification" from the
@ -943,8 +896,6 @@ private void removeSubscription(Subscription subscription)
* <tt>Response</tt> s thus allowing implementers to tap into the
* general event package subscription operations and provide the event
* package-specific processing.
*
* @author Lubomir Marinov
*/
public static abstract class Subscription
extends EventPackageSupport.Subscription

@ -41,13 +41,6 @@ public class EventPackageSubscriber
*/
private final int refreshMargin;
/**
* The list of subscriptions managed by this instance and indexed by their
* CallId.
*/
private final Map<String, Subscription> subscriptions
= new HashMap<String, Subscription>();
/**
* A reference to the <tt>SipMessageFactory</tt> instance that we should
* use when creating requests.
@ -101,25 +94,6 @@ public EventPackageSubscriber(
this.messageFactory = protocolProvider.getMessageFactory();
}
/**
* Adds a specific <tt>Subscription</tt> associated with a specific
* CallId to the list of subscriptions managed by this instance.
*
* @param callId
* the CallId associated with the <tt>Subscription</tt> to be
* added
* @param subscription
* the <tt>Subscription</tt> to be added to the list of
* subscriptions managed by this instance
*/
private void addSubscription(String callId, Subscription subscription)
{
synchronized (subscriptions)
{
subscriptions.put(callId, subscription);
}
}
/**
* Creates a new SUBSCRIBE request in the form of a
* <tt>ClientTransaction</tt> with the parameters of a specific
@ -336,82 +310,66 @@ private ClientTransaction createSubscription(
}
/**
* Gets the <tt>Subscription</tt> from the list of subscriptions managed
* by this instance which is associated with a specific subscription
* Gets the <tt>Subscription</tt> from the list of subscriptions managed by
* this instance which is associated with a specific subscription
* <tt>Address</tt>/Request URI and has a specific id tag in its Event
* header.
*
* @param toAddress
* the subscription <tt>Address</tt>/Request URI of the
* <tt>Subscription</tt> to be retrieved
* @param eventId
* the id tag placed in the Event header of the
* <tt>Subscription</tt> to be retrieved if there is one or
* <tt>null</tt> if the <tt>Subscription</tt> should have no
* id tag in its Event header
* @return an existing <tt>Subscription</tt> from the list of
* subscriptions managed by this instance with the specified
* subscription <tt>Address</tt>/Request URI and the specified
* id tag in its Event header; <tt>null</tt> if no such
* <tt>Subscription</tt> exists in the list of subscriptions
* managed by this instance
* @param toAddress the subscription <tt>Address</tt>/Request URI of the
* <tt>Subscription</tt> to be retrieved
* @param eventId the id tag placed in the Event header of the
* <tt>Subscription</tt> to be retrieved if there is one or <tt>null</tt> if
* the <tt>Subscription</tt> should have no id tag in its Event header
* @return an existing <tt>Subscription</tt> from the list of subscriptions
* managed by this instance with the specified subscription
* <tt>Address</tt>/Request URI and the specified id tag in its Event
* header; <tt>null</tt> if no such <tt>Subscription</tt> exists in the list
* of subscriptions managed by this instance
*/
private Subscription getSubscription(Address toAddress, String eventId)
@Override
protected Subscription getSubscription(Address toAddress, String eventId)
{
synchronized (subscriptions)
{
for (Subscription subscription : subscriptions.values())
if (subscription.equals(toAddress, eventId))
return subscription;
}
return null;
return (Subscription) super.getSubscription(toAddress, eventId);
}
/**
* Gets the <tt>Subscription</tt> from the list of subscriptions managed
* by this instance which is associated with a specific CallId.
* Gets the <tt>Subscription</tt> from the list of subscriptions managed by
* this instance which is associated with a specific CallId.
*
* @param callId
* the CallId associated with the <tt>Subscription</tt> to be
* retrieved
* @return an existing <tt>Subscription</tt> from the list of
* subscriptions managed by this instance which is associated with
* the specified CallId; <tt>null</tt> if no such
* <tt>Subscription</tt> exists in the list of subscriptions
* managed by this instance
* @param callId the CallId associated with the <tt>Subscription</tt> to be
* retrieved
* @return an existing <tt>Subscription</tt> from the list of subscriptions
* managed by this instance which is associated with the specified CallId;
* <tt>null</tt> if no such <tt>Subscription</tt> exists in the list of
* subscriptions managed by this instance
*/
private Subscription getSubscription(String callId)
@Override
protected Subscription getSubscription(String callId)
{
synchronized (subscriptions)
{
return subscriptions.get(callId);
}
return (Subscription) super.getSubscription(callId);
}
/**
* Adds a specific <tt>Subscription</tt> to the list of subscriptions
* managed by this instance only if another <tt>Subscription</tt> with
* the same subscription <tt>Address</tt>/Request URI and id tag of its
* managed by this instance only if another <tt>Subscription</tt> with the
* same subscription <tt>Address</tt>/Request URI and id tag of its
* associated Event header does not exist in the list.
*
* @param subscription
* the new <tt>Subscription</tt> to be added to the list of
* subscriptions managed by this instance if there is no other
* <tt>Subscription</tt> in the list which has the same
* subscription <tt>Address</tt>/Request URI and id tag of
* its Event header
* @param subscription the new <tt>Subscription</tt> to be added to the list
* of subscriptions managed by this instance if there is no other
* <tt>Subscription</tt> in the list which has the same subscription
* <tt>Address</tt>/Request URI and id tag of its Event header
* @throws OperationFailedException if we fail constructing or sending the
* subscription request.
* subscription request
*/
public void poll(Subscription subscription)
throws OperationFailedException
{
if (getSubscription(
subscription.getAddress(), subscription.getEventId())
subscription.getAddress(),
subscription.getEventId())
== null)
{
subscribe(subscription);
}
}
/**
@ -916,93 +874,6 @@ else if ((statusCode == Response.UNAUTHORIZED)
return true;
}
/**
* Removes a <tt>Subscription</tt> from the list of subscriptions
* managed by this instance which is associated with a specific subscription
* <tt>Address</tt>/Request URI and has an id tag in its Event header of
* <tt>null</tt>. If such an instance is not found, does nothing.
*
* @param toAddress
* the subscription <tt>Address</tt>/Request URI of the
* <tt>Subscription</tt> to be removed
*/
public void removeSubscription(Address toAddress)
{
removeSubscription(toAddress, null);
}
/**
* Removes a <tt>Subscription</tt> from the list of subscriptions managed by
* this instance which is associated with a specific subscription
* <tt>Address</tt>/Request URI and has a specific id tag in its Event
* header. If such an instance is not found, does nothing.
*
* @param toAddress the subscription <tt>Address</tt>/Request URI of the
* <tt>Subscription</tt> to be removed
* @param eventId the id tag in the Event header of the
* <tt>Subscription</tt> to be removed; <tt>null</tt> if the
* <tt>Subscription</tt> should have no id tag in its Event header
* @return <tt>true</tt> if a <tt>Subscription</tt> was indeed removed by
* the call; otherwise, <tt>false</tt>
*/
public boolean removeSubscription(Address toAddress, String eventId)
{
boolean removed = false;
synchronized (subscriptions)
{
Iterator<Map.Entry<String, Subscription>> subscriptionIter
= subscriptions.entrySet().iterator();
while (subscriptionIter.hasNext())
{
Map.Entry<String, Subscription> subscriptionEntry
= subscriptionIter.next();
Subscription subscription = subscriptionEntry.getValue();
if (subscription.equals(toAddress, eventId))
{
subscriptionIter.remove();
removed = true;
subscription.removed();
}
}
}
return removed;
}
/**
* Removes a specific <tt>Subscription</tt> from the list of subscriptions
* managed by this instance if it is associated with a specific CallId. If the
* specified <tt>Subscription</tt> is not associated with the specified
* CallId (including the case of no known association for the specified
* CallId), does nothing.
*
* @param callId the CallId which is expected to be associated with the
* specified <tt>Subscription</tt>
* @param subscription the <tt>Subscription</tt> to be removed from the list
* of subscriptions managed by this instance if it is associated with the
* specified CallId
* @return <tt>true</tt> if a <tt>Subscription</tt> was indeed removed by
* the call; otherwise, <tt>false</tt>
*/
private boolean removeSubscription(String callId, Subscription subscription)
{
synchronized (subscriptions)
{
Subscription subscriptionToRemove = subscriptions.get(callId);
if ((subscriptionToRemove != null)
&& subscriptionToRemove.equals(subscription))
{
subscription = subscriptions.remove(callId);
subscription.removed();
return true;
}
}
return false;
}
/**
* Creates and sends a SUBSCRIBE request to the subscription
* <tt>Address</tt>/Request URI of a specific <tt>Subscription</tt>

@ -62,6 +62,13 @@ public class EventPackageSupport
*/
protected final int subscriptionDuration;
/**
* The list of subscriptions managed by this instance and indexed by their
* CallId.
*/
private final Map<String, Subscription> subscriptions
= new HashMap<String, Subscription>();
/**
* The <code>Timer</code> support which executes the time-based tasks of
* this instance.
@ -125,6 +132,27 @@ public EventPackageSupport(
this.protocolProvider.registerMethodProcessor(Request.NOTIFY, this);
}
/**
* Adds a specific <tt>Subscription</tt> associated with a specific CallId
* to the list of subscriptions managed by this instance.
*
* @param callId the CallId associated with the <tt>Subscription</tt> to be
* added
* @param subscription the <tt>Subscription</tt> to be added to the list of
* subscriptions managed by this instance
*/
protected void addSubscription(String callId, Subscription subscription)
{
synchronized (subscriptions)
{
Subscription existingSubscription = subscriptions.get(callId);
if (existingSubscription != null)
removeSubscription(callId, existingSubscription);
subscriptions.put(callId, subscription);
}
}
/**
* Safely returns the <code>ServerTransaction</code> associated with a
* specific <code>RequestEvent</code> or creates a new one if the specified
@ -173,6 +201,73 @@ static ServerTransaction getOrCreateServerTransaction(
return serverTransaction;
}
/**
* Gets the <tt>Subscription</tt> from the list of subscriptions managed by
* this instance which is associated with a specific subscription
* <tt>Address</tt>/Request URI and has a specific id tag in its Event
* header.
*
* @param toAddress the subscription <tt>Address</tt>/Request URI of the
* <tt>Subscription</tt> to be retrieved
* @param eventId the id tag placed in the Event header of the
* <tt>Subscription</tt> to be retrieved if there is one or <tt>null</tt> if
* the <tt>Subscription</tt> should have no id tag in its Event header
* @return an existing <tt>Subscription</tt> from the list of subscriptions
* managed by this instance with the specified subscription
* <tt>Address</tt>/Request URI and the specified id tag in its Event
* header; <tt>null</tt> if no such <tt>Subscription</tt> exists in the list
* of subscriptions managed by this instance
*/
protected Subscription getSubscription(Address toAddress, String eventId)
{
synchronized (subscriptions)
{
for (Subscription subscription : subscriptions.values())
if (subscription.equals(toAddress, eventId))
return subscription;
}
return null;
}
/**
* Gets the <tt>Subscription</tt> from the list of subscriptions managed by
* this instance which is associated with a specific CallId.
*
* @param callId the CallId associated with the <tt>Subscription</tt> to be
* retrieved
* @return an existing <tt>Subscription</tt> from the list of subscriptions
* managed by this instance which is associated with the specified CallId;
* <tt>null</tt> if no such <tt>Subscription</tt> exists in the list of
* subscriptions managed by this instance
*/
protected Subscription getSubscription(String callId)
{
synchronized (subscriptions)
{
return subscriptions.get(callId);
}
}
/**
* Gets a new copy of the list of <tt>Subscription</tt>s managed by this
* instance.
*
* @return a new copy of the list of <tt>Subscription</tt>s managed by this
* instance; if this instance currently manages no <tt>Subscription</tt>s,
* an empty array of <tt>Subscription</tt> element type
*/
protected Subscription[] getSubscriptions()
{
synchronized (this.subscriptions)
{
Collection<Subscription> subscriptions
= this.subscriptions.values();
return
subscriptions.toArray(new Subscription[subscriptions.size()]);
}
}
/**
* Attempts to re-generate a <code>Request</code> within a specific
* <code>ClientTransaction</code> with the proper authorization headers.
@ -257,6 +352,92 @@ static void processAuthenticationChallenge(
}
}
/**
* Removes a <tt>Subscription</tt> from the list of subscriptions managed by
* this instance which is associated with a specific subscription
* <tt>Address</tt>/Request URI and has an id tag in its Event header of
* <tt>null</tt>. If such an instance is not found, does nothing.
*
* @param toAddress the subscription <tt>Address</tt>/Request URI of the
* <tt>Subscription</tt> to be removed
*/
public void removeSubscription(Address toAddress)
{
removeSubscription(toAddress, null);
}
/**
* Removes a <tt>Subscription</tt> from the list of subscriptions managed by
* this instance which is associated with a specific subscription
* <tt>Address</tt>/Request URI and has a specific id tag in its Event
* header. If such an instance is not found, does nothing.
*
* @param toAddress the subscription <tt>Address</tt>/Request URI of the
* <tt>Subscription</tt> to be removed
* @param eventId the id tag in the Event header of the
* <tt>Subscription</tt> to be removed; <tt>null</tt> if the
* <tt>Subscription</tt> should have no id tag in its Event header
* @return <tt>true</tt> if a <tt>Subscription</tt> was indeed removed by
* the call; otherwise, <tt>false</tt>
*/
public boolean removeSubscription(Address toAddress, String eventId)
{
boolean removed = false;
synchronized (subscriptions)
{
Iterator<Map.Entry<String, Subscription>> subscriptionIter
= subscriptions.entrySet().iterator();
while (subscriptionIter.hasNext())
{
Map.Entry<String, Subscription> subscriptionEntry
= subscriptionIter.next();
Subscription subscription = subscriptionEntry.getValue();
if (subscription.equals(toAddress, eventId))
{
subscriptionIter.remove();
removed = true;
subscription.removed();
}
}
}
return removed;
}
/**
* Removes a specific <tt>Subscription</tt> from the list of subscriptions
* managed by this instance if it is associated with a specific CallId. If the
* specified <tt>Subscription</tt> is not associated with the specified
* CallId (including the case of no known association for the specified
* CallId), does nothing.
*
* @param callId the CallId which is expected to be associated with the
* specified <tt>Subscription</tt>
* @param subscription the <tt>Subscription</tt> to be removed from the list
* of subscriptions managed by this instance if it is associated with the
* specified CallId
* @return <tt>true</tt> if a <tt>Subscription</tt> was indeed removed by
* the call; otherwise, <tt>false</tt>
*/
protected boolean removeSubscription(String callId, Subscription subscription)
{
synchronized (subscriptions)
{
Subscription subscriptionToRemove = subscriptions.get(callId);
if ((subscriptionToRemove != null)
&& subscriptionToRemove.equals(subscription))
{
subscription = subscriptions.remove(callId);
subscription.removed();
return true;
}
}
return false;
}
/**
* Represents a general event package subscription in the sense of RFC 3265
* "Session Initiation Protocol (SIP)-Specific Event Notification" and its

Loading…
Cancel
Save