Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Conditions Of Use
    *
    * This software was developed by employees of the National Institute of
    * Standards and Technology (NIST), an agency of the Federal Government.
    * Pursuant to title 15 Untied States Code Section 105, works of NIST
    * employees are not subject to copyright protection in the United States
    * and are considered to be in the public domain.  As a result, a formal
    * license is not needed to use the software.
   *
   * This software is provided by NIST as a service and is expressly
   * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
   * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
   * AND DATA ACCURACY.  NIST does not warrant or make any representations
   * regarding the use of the software or the results thereof, including but
   * not limited to the correctness, accuracy, reliability or usefulness of
   * the software.
   *
   * Permission to use this software is contingent upon your acceptance
   * of the terms of this agreement
   *
   * .
   *
   */
  package gov.nist.javax.sip.stack;
  
  
  import java.util.HashSet;
  import java.util.Set;
  
  import javax.sip.Dialog;
  
  /*
   * Jeff Keyser : architectural suggestions and contributions. Pierre De Rop and Thomas Froment :
   * Bug reports. Jeyashankher < jai@lucent.com > : bug reports. Jeroen van Bemmel : Bug fixes.
   *
   *
   */

This is the sip stack. It is essentially a management interface. It manages the resources for the JAIN-SIP implementation. This is the structure that is wrapped by the SipStackImpl.

Author(s):
M. Ranganathan
Version:
1.2 $Revision: 1.180 $ $Date: 2010-12-02 22:04:15 $
See also:
gov.nist.javax.sip.SipStackImpl
 
 public abstract class SIPTransactionStack implements
 	private static StackLogger logger = CommonLogger.getLogger(SIPTransactionStack.class);
     /*
      * Number of milliseconds between timer ticks (500).
      */
     public static final int BASE_TIMER_INTERVAL = 500;
 
     /*
      * Connection linger time (seconds) this is the time (in seconds) for which
      * we linger the TCP connection before closing it.
      */
     public static final int CONNECTION_LINGER_TIME = 8;
 
     /*
      * Dialog Early state timeout duration.
      */
     protected int earlyDialogTimeout = 180;
 
 
     /*
      * Table of retransmission Alert timers.
      */
 
     // Table of early dialogs ( to keep identity mapping )
 
     // Table of dialogs.
 
     // Table of server dialogs ( for loop detection)
 
     // A set of methods that result in dialog creations.
     protected static final Set<StringdialogCreatingMethods = new HashSet<String>();
 
     // Global timer. Use this for all timer tasks.
 
     private SipTimer timer;
 
     // List of pending server transactions
 
     // hashtable for fast lookup
 
     // Set to false if you want hiwat and lowat to be consulted.
     protected boolean unlimitedServerTransactionTableSize = true;
 
     // Set to false if you want unlimited size of client trnansactin table.
     protected boolean unlimitedClientTransactionTableSize = true;
 
     // High water mark for ServerTransaction Table
     // after which requests are dropped.
     protected int serverTransactionTableHighwaterMark = 5000;
 
     // Low water mark for Server Tx table size after which
     // requests are selectively dropped
     protected int serverTransactionTableLowaterMark = 4000;
 
     // Hiwater mark for client transaction table. These defaults can be
     // overriden by stack
     // configuration.
     protected int clientTransactionTableHiwaterMark = 1000;
 
     // Low water mark for client tx table.
     protected int clientTransactionTableLowaterMark = 800;
 
 
     // Hashtable for server transactions.
 
     // A table of ongoing transactions indexed by mergeId ( for detecting merged
     // requests.
 
 
 
     protected boolean deliverRetransmittedAckToListener = false;
 
     /*
      * ServerLog is used just for logging stack message tracecs.
      */
     protected ServerLogger serverLogger;
 
     /*
      * We support UDP on this stack.
      */
     boolean udpFlag;
 
     /*
      * Internal router. Use this for all sip: request routing.
      */
     protected DefaultRouter defaultRouter;
 
     /*
      * Global flag that turns logging off
      */
     protected boolean needsLogging;
 
     /*
      * Flag used for testing TI, bypasses filtering of ACK to non-2xx
      */
     private boolean non2XXAckPassedToListener;
 
     /*
      * Class that handles caching of TCP/TLS connections.
      */
     protected IOHandler ioHandler;
 
     /*
      * Flag that indicates that the stack is active.
      */
     protected boolean toExit;
 
     /*
      * Name of the stack.
      */
     protected String stackName;
 
     /*
      * IP address of stack -- this can be re-written by stun.
      *
      * @deprecated
      */
     protected String stackAddress;
 
     /*
      * INET address of stack (cached to avoid repeated lookup)
      *
      * @deprecated
      */
     protected InetAddress stackInetAddress;
 
     /*
      * Request factory interface (to be provided by the application)
      */
 
     /*
      * Router to determine where to forward the request.
      */
     protected javax.sip.address.Router router;
 
     /*
      * Number of pre-allocated threads for processing udp messages. -1 means no
      * preallocated threads ( dynamically allocated threads).
      */
     protected int threadPoolSize;
 
     /*
      * max number of simultaneous connections.
      */
     protected int maxConnections;
 
     /*
      * Close accept socket on completion.
      */
     protected boolean cacheServerConnections;
 
     /*
      * Close connect socket on Tx termination.
      */
     protected boolean cacheClientConnections;
 
     /*
      * Use the user supplied router for all out of dialog requests.
      */
     protected boolean useRouterForAll;
 
     /*
      * Max size of message that can be read from a TCP connection.
      */
     protected int maxContentLength;
 
     /*
      * Max # of headers that a SIP message can contain.
      */
     protected int maxMessageSize;
 
     /*
      * A collection of message processors.
      */
 
     /*
      * Read timeout on TCP incoming sockets -- defines the time between reads
      * for after delivery of first byte of message.
      */
     protected int readTimeout;
 
     /*
      * The socket factory. Can be overriden by applications that want direct
      * access to the underlying socket.
      */
 
     protected NetworkLayer networkLayer;
 
     /*
      * Outbound proxy String ( to be handed to the outbound proxy class on
      * creation).
      */
     protected String outboundProxy;
 
     protected String routerPath;
 
     // Flag to indicate whether the stack will provide dialog
     // support.
     protected boolean isAutomaticDialogSupportEnabled;
 
     // The set of events for which subscriptions can be forked.
 
     protected HashSet<StringforkedEvents;
 
     // Generate a timestamp header for retransmitted requests.
     protected boolean generateTimeStampHeader;
 
     protected AddressResolver addressResolver;
 
     // Max time that the listener is allowed to take to respond to a
     // request. Default is "infinity". This property allows
     // containers to defend against buggy clients (that do not
     // want to respond to requests).
     protected int maxListenerResponseTime;
 
     // A flag that indicates whether or not RFC 2543 clients are fully
     // supported.
     // If this is set to true, then To tag checking on the Dialog layer is
     // disabled in a few places - resulting in possible breakage of forked
     // dialogs.
     protected boolean rfc2543Supported = true;
 
     // / Provides a mechanism for applications to check the health of threads in
     // the stack
     protected ThreadAuditor threadAuditor = new ThreadAuditor();
 
     protected LogRecordFactory logRecordFactory;
 
     // Set to true if the client CANCEL transaction should be checked before
     // sending
     // it out.
     protected boolean cancelClientTransactionChecked = true;
 
     // Is to tag reassignment allowed.
     protected boolean remoteTagReassignmentAllowed = true;
 
     protected boolean logStackTraceOnMessageSend = true;
 
     // Receive UDP buffer size
     protected int receiveUdpBufferSize;
 
     // Send UDP buffer size
     protected int sendUdpBufferSize;
 
     protected int stackCongenstionControlTimeout = 0;
 
     protected boolean isBackToBackUserAgent = false;
 
     protected boolean checkBranchId;
 
     protected boolean isAutomaticDialogErrorHandlingEnabled = true;
 
     protected boolean isDialogTerminatedEventDeliveredForNullDialog = false;
 
     // Max time for a forked response to arrive. After this time, the original
     // dialog
     // is not tracked. If you want to track the original transaction you need to
     // specify
     // the max fork time with a stack init property.
     protected int maxForkTime = 0;
 
     // Whether or not to deliver unsolicited NOTIFY
 
     private boolean deliverUnsolicitedNotify = false;
 
     private boolean deliverTerminatedEventForAck = false;
 
     
     // ThreadPool when parsed SIP messages are processed. Affects the case when many TCP calls use single socket.
     private int tcpPostParsingThreadPoolSize = 0;
 
     // Minimum time between NAT kee alive pings from clients.
     // Any ping that exceeds this time will result in  CRLF CRLF going
     // from the UDP message channel.
     protected long minKeepAliveInterval = -1L;
 
     // The time after which a "dialog timeout event" is delivered to a listener.
     protected int dialogTimeoutFactor = 64;
 
     // factory used to create MessageParser objects
     // factory used to create MessageProcessor objects
 
     protected boolean aggressiveCleanup = false;
 
     
 
     protected static Executor selfRoutingThreadpoolExecutor;
 
     private int threadPriority = .;
     
     private static class SameThreadExecutor implements Executor {
 
         public void execute(Runnable command) {
             command.run(); // Just run the command is the same thread
         }
 
     }
 
         if( == null) {
             if(this.<=0) {
                  = new SameThreadExecutor();
             } else {
                  = Executors.newFixedThreadPool(this.new ThreadFactory() {
                     private int threadCount = 0;
 
                     public Thread newThread(Runnable pRunnable) {
                     	Thread thread = new Thread(pRunnable, String.format("%s-%d",
                                         "SelfRoutingThread"++));
                     	thread.setPriority();
                     	return thread;
                     }
                 });
             }
         }
         return ;
     }
      
Executor used to optimise the ReinviteSender Runnable in the sendRequest of the SipDialog
 
     private ExecutorService reinviteExecutor = Executors
             .newCachedThreadPool(new ThreadFactory() {
         private int threadCount = 0;
 
         public Thread newThread(Runnable pRunnable) {
                     return new Thread(pRunnable, String.format("%s-%d",
                             "ReInviteSender"++));
         }
     });
 
     // / Timer to regularly ping the thread auditor (on behalf of the timer
     // thread)
     protected class PingTimer extends SIPStackTimerTask {
         // / Timer thread handle
 
         // / Constructor
         public PingTimer(ThreadAuditor.ThreadHandle a_oThreadHandle) {
              = a_oThreadHandle;
         }
 
         public void runTask() {
             // Check if we still have a timer (it may be null after shutdown)
             if (getTimer() != null) {
                 // Register the timer task if we haven't done so
                 if ( == null) {
                     // This happens only once since the thread handle is passed
                     // to the next scheduled ping timer
                      = getThreadAuditor().addCurrentThread();
                 }
 
                 // Let the thread auditor know that the timer task is alive
                 .ping();
 
                 // Schedule the next ping
                 getTimer().schedule(new PingTimer(),
                         .getPingIntervalInMillisecs());
             }
         }
 
     }
 
 
 
         private final String forkId;
 
         public RemoveForkedTransactionTimerTask(String forkId) {
             this. = forkId;
         }
 
         @Override
         public void runTask() {
             .remove();
         }
 
     }
 
     static {
         // Standard set of methods that create dialogs.
     }

    
Default constructor.
 
     protected SIPTransactionStack() {
         this. = false;
         this. = new HashSet<String>();
         // set of events for which subscriptions can be forked.
         // Set an infinite thread pool size.
         this. = -1;
         // Close response socket after infinte time.
         // for max performance
         this. = true;
         // Close the request socket after infinite time.
         // for max performance
         this. = true;
         // Max number of simultaneous connections.
         this. = -1;
         // Array of message processors.
         // jeand : using concurrent data structure to avoid excessive blocking
         // Handle IO for this process.
         this. = new IOHandler(this);
 
         // The read time out is infinite.
         this. = -1;
 
         this. = -1;
 
         // The default (identity) address lookup scheme
 
         this. = new DefaultAddressResolver();
 
         // Notify may or may not create a dialog. This is handled in
         // the code.
         // Create the transaction collections
 
         // Dialog dable.
         this. = new ConcurrentHashMap<StringSIPDialog>();
         this. = new ConcurrentHashMap<StringSIPDialog>();
         this. = new ConcurrentHashMap<StringSIPDialog>();
 
 
         // Start the timer event thread.
 
 //        this.timer = new DefaultTimer();
 
 
     }

    
Re Initialize the stack instance.
 
     protected void reInit() {
             .logDebug("Re-initializing !");
 
         // Array of message processors.
         // Handle IO for this process.
         this. = new IOHandler(this);
         // Dialog dable.
         this. = new ConcurrentHashMap<StringSIPDialog>();
         this. = new ConcurrentHashMap<StringSIPDialog>();
         this. = new ConcurrentHashMap<StringSIPDialog>();
 
 //        this.timer = new DefaultTimer();
 
         this. = new AtomicInteger(0);
 
     }

    
Creates and binds, if necessary, a socket connected to the specified destination address and port and then returns its local address.

Parameters:
dst the destination address that the socket would need to connect to.
dstPort the port number that the connection would be established with.
localAddress the address that we would like to bind on (null for the "any" address).
localPort the port that we'd like our socket to bind to (0 for a random port).
Returns:
the SocketAddress that this handler would use when connecting to the specified destination address and port.
Throws:
java.io.IOException if binding the socket fails
 
     public SocketAddress getLocalAddressForTcpDst(InetAddress dstint dstPort,
             InetAddress localAddressint localPortthrows IOException {
         return this..getLocalAddressForTcpDst(
                         dstdstPortlocalAddresslocalPort);
     }

    
Creates and binds, if necessary, a TCP SSL socket connected to the specified destination address and port and then returns its local address.

Parameters:
dst the destination address that the socket would need to connect to.
dstPort the port number that the connection would be established with.
localAddress the address that we would like to bind on (null for the "any" address).
Returns:
the SocketAddress that this handler would use when connecting to the specified destination address and port.
Throws:
java.io.IOException if binding the socket fails
 
              int dstPortInetAddress localAddressthrows IOException {
 
         // First find the TLS message processor
         TLSMessageProcessor tlsProcessor = null;
         MessageProcessor[] processors = getMessageProcessors();
         for (MessageProcessor processor : processors){
             if(processor instanceof TLSMessageProcessor){
                 tlsProcessor = (TLSMessageProcessor)processor;
                 break;
             }
         }
 
         if(tlsProcessor == null)
             return null;
 
         // Here we don't create the channel but if the channel is already
         // existing will be returned
         TLSMessageChannel msgChannel =
             (TLSMessageChannel)tlsProcessor.createMessageChannel(dstdstPort);
 
         return this..getLocalAddressForTlsDst(
             dstdstPortlocalAddressmsgChannel);
      }

    
For debugging -- allows you to disable logging or enable logging selectively.
 
     public void disableLogging() {
         this..disableLogging();
     }

    
Globally enable message logging ( for debugging)
 
     public void enableLogging() {
         this..enableLogging();
     }

    
Print the dialog table.
 
     public void printDialogTable() {
         if (.isLoggingEnabled(.)) {
             this..logDebug(
                     "dialog table  = " + this.);
         }
     }

    
Retrieve a transaction from our table of transactions with pending retransmission alerts.

Parameters:
dialogId
Returns:
-- the RetransmissionAlert enabled transaction corresponding to the given dialog ID.
 
             String dialogId) {
                 .get(dialogId);
     }

    
Return true if extension is supported.

Returns:
true if extension is supported and false otherwise.
 
     public static boolean isDialogCreated(String method) {
         return .contains(method);
     }

    
Add an extension method.

Parameters:
extensionMethod -- extension method to support for dialog creation
 
     public void addExtensionMethod(String extensionMethod) {
         if (extensionMethod.equals(.)) {
             if (.isLoggingEnabled(.))
                 .logDebug("NOTIFY Supported Natively");
         } else {
             .add(extensionMethod.trim().toUpperCase());
         }
     }

    
Put a dialog into the dialog table.

Parameters:
dialog -- dialog to put into the dialog table.
 
     public SIPDialog putDialog(SIPDialog dialog) {
         String dialogId = dialog.getDialogId();
         if (.containsKey(dialogId)) {
             if (.isLoggingEnabled(.)) {
                 
                         .logDebug("putDialog: dialog already exists" + dialogId
                                 + " in table = " + .get(dialogId));
             }
             return .get(dialogId);
         }
         if (.isLoggingEnabled(.)) {
             .logDebug("putDialog dialogId=" + dialogId
                     + " dialog = " + dialog);
         }
         dialog.setStack(this);
             .logStackTrace();
         .put(dialogIddialog);
         putMergeDialog(dialog);
         
         return dialog;
     }

    
Create a dialog and add this transaction to it.

Parameters:
transaction -- tx to add to the dialog.
Returns:
the newly created Dialog.
 
    
Create a dialog and add this transaction to it.

Parameters:
transaction -- tx to add to the dialog.
Returns:
the newly created Dialog.
 
     public SIPDialog createDialog(SIPTransaction transaction) {
 
         SIPDialog retval = null;
 
         if (transaction instanceof SIPClientTransaction) {
             String dialogId = ((SIPRequesttransaction.getRequest())
                     .getDialogId(false);
             if (.isLoggingEnabled(.)) {
                 .logDebug("createDialog dialogId=" + dialogId);
             }
             if (this..get(dialogId) != null) {
                 SIPDialog dialog = this..get(dialogId);
                 if (dialog.getState() == null
                         || dialog.getState() == .) {
                     retval = dialog;
                     if (.isLoggingEnabled(.)) {
                         .logDebug("createDialog early Dialog found : earlyDialogId="
                                 + dialogId + " earlyDialog= " + dialog);
                     }
                 } else {
                     retval = new SIPDialog(transaction);
                     this..put(dialogIdretval);
                 }
             } else {
                 retval = new SIPDialog(transaction);
                 this..put(dialogIdretval);
                 if (.isLoggingEnabled(.)) {
                     .logDebug("createDialog early Dialog not found : earlyDialogId="
                             + dialogId + " created one " + retval);
                 }
             }
         } else {
             retval = new SIPDialog(transaction);
         }
 
         return retval;
 
     }

    
Create a Dialog given a client tx and response.

Parameters:
transaction
sipResponse
Returns:
 
 
     public SIPDialog createDialog(SIPClientTransaction transaction,
             SIPResponse sipResponse) {
         String originalDialogId = ((SIPRequest)transaction.getRequest()).getDialogId(false);
         String earlyDialogId = sipResponse.getDialogId(false);
         if (.isLoggingEnabled(.)) {
             .logDebug("createDialog originalDialogId=" + originalDialogId);
             .logDebug("createDialog earlyDialogId=" + earlyDialogId);
             .logDebug("createDialog default Dialog=" + transaction.getDefaultDialog());
             if(transaction.getDefaultDialog() != null) {
                 .logDebug("createDialog default Dialog Id=" + transaction.getDefaultDialog().getDialogId());
             }
         }
         SIPDialog retval = null;
         SIPDialog earlyDialog = this..get(originalDialogId);
         if (earlyDialog != null && transaction != null && (transaction.getDefaultDialog() == null || transaction.getDefaultDialog().getDialogId().equals(originalDialogId))) {
             retval = earlyDialog;
             if (.isLoggingEnabled(.)) {
                 .logDebug("createDialog early Dialog found : earlyDialogId="
                         + originalDialogId + " earlyDialog= " + retval);
             }
             if (sipResponse.isFinalResponse()) {
                 this..remove(originalDialogId);
             }
 
         } else {
             retval = new SIPDialog(transactionsipResponse);
             if (.isLoggingEnabled(.)) {
                 .logDebug("createDialog early Dialog not found : earlyDialogId="
                         + earlyDialogId + " created one " + retval);
             }
         }
         return retval;
 
     }
    
Create a Dialog given a sip provider and response.

Parameters:
sipProvider
sipResponse
Returns:
 
     public SIPDialog createDialog(SipProviderImpl sipProvider,
             SIPResponse sipResponse) {
         return new SIPDialog(sipProvidersipResponse);
     }

    
Remove the dialog from the dialog table.

Parameters:
dialog -- dialog to remove.
 
     public void removeDialog(SIPDialog dialog) {
 
         String id = dialog.getDialogId();
 
         String earlyId = dialog.getEarlyDialogId();
 
         if (earlyId != null) {
             this..remove(earlyId);
             this..remove(earlyId);
         }
 
         removeMergeDialog(dialog.getMergeId());
 
         if (id != null) {
 
             // FHT: Remove dialog from table only if its associated dialog is
             // the same as the one
             // specified
 
             Object old = this..get(id);
 
             if (old == dialog) {
                 this..remove(id);
             }
 
             // We now deliver DTE even when the dialog is not originally present
             // in the Dialog
             // Table
             // This happens before the dialog state is assigned.
 
             if (!dialog.testAndSetIsDialogTerminatedEventDelivered()) {
                 DialogTerminatedEvent event = new DialogTerminatedEvent(dialog
                         .getSipProvider(), dialog);
 
                 // Provide notification to the listener that the dialog has
                 // ended.
                 dialog.getSipProvider().handleEvent(eventnull);
 
             }
 
         } else if ( this. ) {
             if (!dialog.testAndSetIsDialogTerminatedEventDelivered()) {
                 DialogTerminatedEvent event = new DialogTerminatedEvent(dialog
                         .getSipProvider(), dialog);
 
                 // Provide notification to the listener that the dialog has
                 // ended.
                 dialog.getSipProvider().handleEvent(eventnull);
 
             }
         }
 
     }
 
     public SIPDialog getEarlyDialog(String dialogId) {
 
         SIPDialog sipDialog = (SIPDialog.get(dialogId);
         if (.isLoggingEnabled(.)) {
             .logDebug("getEarlyDialog(" + dialogId + ") : returning " + sipDialog);
         }
         return sipDialog;
 
     }
 
     protected void removeMergeDialog(String mergeId) {
 		if(mergeId != null) {
 				.logDebug("Tyring to remove Dialog from serverDialogMerge table with Merge Dialog Id " + mergeId);
 			}
 			SIPDialog sipDialog = .remove(mergeId);		
 			if (.isLoggingEnabled(.) && sipDialog != null) {
 				.logDebug("removed Dialog " + sipDialog + " from serverDialogMerge table with Merge Dialog Id " + mergeId);
 			}
 		}
 	}
 	
 	protected void putMergeDialog(SIPDialog sipDialog) {
 		if(sipDialog != null) {
 			String mergeId = sipDialog.getMergeId();
 			if(mergeId != null) {
 				.put(mergeIdsipDialog);
 					.logDebug("put Dialog " + sipDialog + " in serverDialogMerge table with Merge Dialog Id " + mergeId);
 				}
 			}
 		}
 	}
    
    
Return the dialog for a given dialog ID. If compatibility is enabled then we do not assume the presence of tags and hence need to add a flag to indicate whether this is a server or client transaction.

Parameters:
dialogId is the dialog id to check.
 
 
     public SIPDialog getDialog(String dialogId) {
 
         SIPDialog sipDialog = (SIPDialog.get(dialogId);
         if (.isLoggingEnabled(.)) {
             .logDebug("getDialog(" + dialogId + ") : returning "
                     + sipDialog);
         }
         return sipDialog;
 
     }

    
Remove the dialog given its dialog id. This is used for dialog id re-assignment only.

Parameters:
dialogId is the dialog Id to remove.
 
     public void removeDialog(String dialogId) {
         if (.isLoggingEnabled()) {
             .logWarning("Silently removing dialog from table");
         }
         .remove(dialogId);
     }

    
Find a matching client SUBSCRIBE to the incoming notify. NOTIFY requests are matched to such SUBSCRIBE requests if they contain the same "Call-ID", a "To" header "tag" parameter which matches the "From" header "tag" parameter of the SUBSCRIBE, and the same "Event" header field. Rules for comparisons of the "Event" headers are described in section 7.2.1. If a matching NOTIFY request contains a "Subscription-State" of "active" or "pending", it creates a new subscription and a new dialog (unless they have already been created by a matching response, as described above).

Parameters:
notifyMessage
Returns:
-- the matching ClientTransaction with semaphore aquired or null if no such client transaction can be found.
            SIPRequest notifyMessageListeningPointImpl listeningPoint) {
        SIPClientTransaction retval = null;
        try {
            Iterator it = .values().iterator();
            if (.isLoggingEnabled(.))
                .logDebug("ct table size = "
                        + .size());
            String thisToTag = notifyMessage.getTo().getTag();
            if (thisToTag == null) {
                return retval;
            }
            Event eventHdr = (EventnotifyMessage.getHeader(.);
            if (eventHdr == null) {
                if (.isLoggingEnabled(.)) {
                    
                            .logDebug("event Header is null -- returning null");
                }
                return retval;
            }
            while (it.hasNext()) {
                SIPClientTransaction ct = (SIPClientTransactionit.next();
                if (!ct.getMethod().equals(.))
                    continue;
                // if ( sipProvider.getListeningPoint(transport) == null)
                String fromTag = ct.getOriginalRequestFromTag();
                Event hisEvent = (Eventct.getOriginalRequestEvent();
                // Event header is mandatory but some slopply clients
                // dont include it.
                if (hisEvent == null)
                    continue;
                if (.isLoggingEnabled(.)) {
                    .logDebug("ct.fromTag = " + fromTag);
                    .logDebug("thisToTag = " + thisToTag);
                    .logDebug("hisEvent = " + hisEvent);
                    .logDebug("eventHdr " + eventHdr);
                }
                if (  fromTag.equalsIgnoreCase(thisToTag)
                      && hisEvent != null
                      && eventHdr.match(hisEvent)
                      && notifyMessage.getCallId().getCallId().equalsIgnoreCase(
                                ct.getOriginalRequestCallId())) {
                    if (!this.isDeliverUnsolicitedNotify() ) {
                        ct.acquireSem();
                    }
                    retval = ct;
                    return ct;
                }
            }
            return retval;
        } finally {
            if (.isLoggingEnabled(.))
                .logDebug("findSubscribeTransaction : returning "
                        + retval);
        }
    }

    
Add entry to "Transaction Pending ACK" table.

Parameters:
serverTransaction
    public void addTransactionPendingAck(SIPServerTransaction serverTransaction) {
        String branchId = ((SIPRequestserverTransaction.getRequest())
                .getTopmostVia().getBranch();
        if ( branchId != null ) {
            this..put(branchId,
                    serverTransaction);
        }
    }

    
Get entry in the server transaction pending ACK table corresponding to an ACK.

Parameters:
ackMessage
Returns:
        return this..get(ackMessage
                .getTopmostVia().getBranch());
    }

    
Remove entry from "Transaction Pending ACK" table.

Parameters:
serverTransaction
Returns:
    public boolean removeTransactionPendingAck(SIPServerTransaction serverTransaction) {
//        String branchId = ((SIPRequest)serverTransaction.getRequest()).getTopmostVia().getBranch();
        String branchId = serverTransaction.getBranchId();
        if ( branchId != null
                && this..
                        containsKey(branchId) ) {
            this..remove(branchId);
            return true;
        } else {
            return false;
        }
    }

    
Check if this entry exists in the "Transaction Pending ACK" table.

Parameters:
serverTransaction
Returns:
    public boolean isTransactionPendingAck(
            SIPServerTransaction serverTransaction) {
        String branchId = ((SIPRequestserverTransaction.getRequest())
                .getTopmostVia().getBranch();
        return this..contains(branchId);
    }

    
Find the transaction corresponding to a given request.

Parameters:
sipMessage request for which to retrieve the transaction.
isServer search the server transaction table if true.
Returns:
the transaction object corresponding to the request or null if no such mapping exists.
    public SIPTransaction findTransaction(SIPMessage sipMessage,
            boolean isServer) {
        SIPTransaction retval = null;
        try {
            if (isServer) {
                Via via = sipMessage.getTopmostVia();
                if (via.getBranch() != null) {
                    String key = sipMessage.getTransactionId();
                    retval = (SIPTransaction.get(key);
                    if (.isLoggingEnabled(.))
                        
                                .logDebug(
                                        "serverTx: looking for key " + key
                                                + " existing="
                                + );
                    if (key
                            .startsWith(.)) {
                        return retval;
                    }
                }
                // Need to scan the table for old style transactions (RFC 2543
                // style)
                Iterator<SIPServerTransactionit = 
                        .values().iterator();
                while (it.hasNext()) {
                    SIPServerTransaction sipServerTransaction = (SIPServerTransactionit
                            .next();
                    if (sipServerTransaction
                            .isMessagePartOfTransaction(sipMessage)) {
                        retval = sipServerTransaction;
                        return retval;
                    }
                }
            } else {
                Via via = sipMessage.getTopmostVia();
                if (via.getBranch() != null) {
                    String key = sipMessage.getTransactionId();
                    if (.isLoggingEnabled(.))
                        .logDebug(
                                "clientTx: looking for key " + key);
                    retval = (SIPTransaction.get(key);
                    if (key
                            .startsWith(.)) {
                        return retval;
                    }
                }
                // Need to scan the table for old style transactions (RFC 2543
                // style). This is terribly slow but we need to do this
                // for backasswords compatibility.
                Iterator<SIPClientTransactionit = 
                        .values().iterator();
                while (it.hasNext()) {
                    SIPClientTransaction clientTransaction = (SIPClientTransactionit
                            .next();
                    if (clientTransaction
                            .isMessagePartOfTransaction(sipMessage)) {
                        retval = clientTransaction;
                        return retval;
                    }
                }
            }
        } finally {
            if (.isLoggingEnabled(.)) {
                .logDebug(
                        "findTransaction: returning  : " + retval);
            }
        }
        return retval;
    }
    public SIPTransaction findTransaction(String transactionIdboolean isServer) {
        if(isServer) {
            return .get(transactionId);
        } else {
            return .get(transactionId);
        }
    }

    
Get the transaction to cancel. Search the server transaction table for a transaction that matches the given transaction.
    public SIPTransaction findCancelTransaction(SIPRequest cancelRequest,
            boolean isServer) {
            .logDebug("findCancelTransaction request= \n"
                    + cancelRequest + "\nfindCancelRequest isServer="
                    + isServer);
        }
        if (isServer) {
            Iterator<SIPServerTransactionli = this.
                    .values().iterator();
            while (li.hasNext()) {
                SIPTransaction transaction = (SIPTransactionli.next();
                SIPServerTransaction sipServerTransaction = (SIPServerTransactiontransaction;
                if (sipServerTransaction
                        .doesCancelMatchTransaction(cancelRequest))
                    return sipServerTransaction;
            }
        } else {
            Iterator<SIPClientTransactionli = this.
                    .values().iterator();
            while (li.hasNext()) {
                SIPTransaction transaction = (SIPTransactionli.next();
                SIPClientTransaction sipClientTransaction = (SIPClientTransactiontransaction;
                if (sipClientTransaction