/* * SIP Communicator, 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.sip; import java.util.*; import javax.sip.*; /** * Implements utility methods to aid the manipulation of {@link Dialog} * instances and extend the mentioned type with additional functionality. * * @author Lubomir Marinov */ public final class DialogUtils { /** * Associates a specific subscription with the a given <code>Dialog</code> * in order to allow it to keep the dialog in question alive even after a * BYE request. * * @param dialog the <code>Dialog</code> to associate the subscription with * and to be kept alive after a BYE request because of the * subscription * @param subscription the subscription to be associated with * <code>dialog</code> and keep it alive after a BYE request * @return <tt>true</tt> if the specified subscription was associated with * the given dialog; <tt>false</tt> if no changes were applied * @throws SipException */ public static boolean addSubscription(Dialog dialog, Object subscription) throws SipException { synchronized (dialog) { DialogApplicationData applicationData = (DialogApplicationData) SipApplicationData.getApplicationData( dialog, SipApplicationData.KEY_SUBSCRIPTIONS); if (applicationData == null) { applicationData = new DialogApplicationData(); SipApplicationData.setApplicationData( dialog, SipApplicationData.KEY_SUBSCRIPTIONS, applicationData); } if (applicationData.addSubscription(subscription)) { try { dialog.terminateOnBye(false); return true; } catch (SipException ex) { /* * Since the subscription didn't quite register, undo the * part of the registration which did succeed. */ applicationData.removeSubscription(subscription); throw ex; } } return false; } } /** * Determines whether a BYE request has already been processed in a specific * <code>Dialog</code> and thus allows determining whether the dialog in * question should be terminated when the last associated subscription is * terminated. * * @param dialog the <code>Dialog</code> to be examined * @return <tt>true</tt> if a BYE request has already been processed in the * specified <code>dialog</code>; <tt>false</tt>, otherwise */ public static boolean isByeProcessed(Dialog dialog) { synchronized (dialog) { DialogApplicationData applicationData = (DialogApplicationData) SipApplicationData.getApplicationData( dialog, SipApplicationData.KEY_SUBSCRIPTIONS); return (applicationData == null) ? false : applicationData .isByeProcessed(); } } /** * Processes a BYE request in a specific <code>Dialog</code> for the * purposes of subscription associations and returns an indicator which * determines whether the specified dialog should still be considered alive * after the processing of the BYE request. * * @param dialog the <code>Dialog</code> in which a BYE request has arrived * @return <tt>true</tt> if <code>dialog</code> should still be considered * alive after processing the mentioned BYE request; <tt>false</tt> * if <code>dialog</code> is to be expected to die after processing * the request in question * @throws SipException */ public static boolean processByeThenIsDialogAlive(Dialog dialog) throws SipException { synchronized (dialog) { DialogApplicationData applicationData = (DialogApplicationData) SipApplicationData.getApplicationData( dialog, SipApplicationData.KEY_SUBSCRIPTIONS); if (applicationData != null) { applicationData.setByeProcessed(true); if (applicationData.getSubscriptionCount() > 0) { dialog.terminateOnBye(false); return true; } } return false; } } /** * Dissociates a specific subscription with a given <code>Dialog</code> in * order to no longer allow it to keep the dialog in question alive even * after a BYE request, deletes the dialog if there are no other * subscriptions associated with it and a BYE request has already been * received and returns an indicator which determines whether the specified * dialog is still alive after the dissociation of the given subscription. * * @param dialog the <code>Dialog</code> to dissociate the subscription with * and to no longer be kept alive after a BYE request because of * the subscription * @param subscription the subscription to be dissociated with * <code>dialog</code> and to no longer be kept alive after a BYE * request because of the subscription * @return <tt>true</tt> if the dialog is still alive after the * dissociation; <tt>false</tt> if the dialog was terminated because * of the dissociation */ public static boolean removeSubscriptionThenIsDialogAlive(Dialog dialog, Object subscription) { synchronized (dialog) { DialogApplicationData applicationData = (DialogApplicationData) SipApplicationData.getApplicationData( dialog, SipApplicationData.KEY_SUBSCRIPTIONS); if ((applicationData != null) && applicationData.removeSubscription(subscription) && (applicationData.getSubscriptionCount() <= 0) && applicationData.isByeProcessed()) { dialog.delete(); return false; } return true; } } /** * Prevents the creation of <code>DialogUtils</code> instances. */ private DialogUtils() { } /** * Represents the application-specific data which the SIP protocol provider * associates with {@link #Dialog} instances. * <p> * The implementation at the time of this writing allows tracking * subscriptions in a specific <code>Dialog</code> in order to make it * possible to determine whether a BYE request should terminate the * respective dialog. * </p> */ private static class DialogApplicationData { /** * The indicator which determines whether a BYE request has already been * processed in the owning <code>Dialog</code> and thus allows * determining whether the dialog in question should be terminated when * the last associated subscription is terminated. */ private boolean byeIsProcessed; /** * The set of subscriptions not yet terminated in the owning * <code>Dialog</code> i.e. keeping it alive even after a BYE request. */ private final List<Object> subscriptions = new ArrayList<Object>(); /** * Associates a specific subscription with the owning * <code>Dialog</code> in order to allow it to keep the dialog in * question alive even after a BYE request. * * @param subscription the subscription with no specific type of * interest to this implementation to be associated with the * owning <code>Dialog</code> * @return <tt>true</tt> if the specified subscription caused a * modification of the list of associated subscriptions; * <tt>false</tt> if no change to the mentioned list was applied */ public boolean addSubscription(Object subscription) { if (!subscriptions.contains(subscription)) { return subscriptions.add(subscription); } return false; } /** * Determines whether a BYE request has already been processed in the * owning <code>Dialog</code> and thus allows determining whether the * dialog in question should be terminated when the last associated * subscription is terminated. * * @return <tt>true</tt> if a BYE request has already been processed in * the owning <code>Dialog</code>; <tt>false</tt>, otherwise */ public boolean isByeProcessed() { return byeIsProcessed; } /** * Determines the number of subscriptions associated with the owning * <code>Dialog</code> i.e. keeping it alive even after a BYE request. * * @return the number of subscriptions associated with the owning * <code>Dialog</code> i.e. keeping it alive even after a BYE * request */ public int getSubscriptionCount() { return subscriptions.size(); } /** * Dissociates a specific subscription with the owning * <code>Dialog</code> in order to no longer allow it to keep the dialog * in question alive even after a BYE request. * * @param subscription the subscription with no specific type of * interest to this implementation to be dissociated with the * owning <code>Dialog</code> * @return <tt>true</tt> if the specified subscription caused a * modification of the list of associated subscriptions; * <tt>false</tt> if no change to the mentioned list was applied */ public boolean removeSubscription(Object subscription) { return subscriptions.remove(subscription); } /** * Sets the indicator which determines whether a BYE request has already * been processed in the owning <code>Dialog</code> and thus allows * determining whether the dialog in question should be terminated when * the last associated subscription is terminated. * * @param byeIsProcessed <tt>true</tt> if a BYE request has already been * processed in the owning <code>Dialog</code>; * <tt>false</tt>, otherwise */ public void setByeProcessed(boolean byeIsProcessed) { this.byeIsProcessed = byeIsProcessed; } } }