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.Date;
  import java.util.HashSet;
  import java.util.Set;
  import java.util.Timer;
  
  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.152.2.6 $ $Date: 2010-12-02 01:41:35 $
See also:
gov.nist.javax.sip.SipStackImpl
 
 public abstract class SIPTransactionStack implements
 
 	/*
 	 * 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;
 
 	/*
 	 * 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 Timer 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;
 
 	/*
 	 * A wrapper around differnt logging implementations (log4j, commons
 	 * logging, slf4j, ...) to help log debug.
 	 */
 
 	/*
 	 * ServerLog is used just for logging stack message tracecs.
 	 */
 
 	/*
 	 * We support UDP on this stack.
 	 */
 	boolean udpFlag;
 
 	/*
 	 * Internal router. Use this for all sip: request routing.
 	 */
 
 	/*
 	 * 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
 	 */
 
 	/*
 	 * 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.
 	 */
 
 
 	/*
 	 * 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;
 
 
 	// 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();
 
 
 	// 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 stackCongestionControlTimeout = 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
Executor used to optimise the ReinviteSender Runnable in the sendRequest of the SipDialog
 
 	
 	private static class SameThreadExecutor implements Executor {
 
 		public void execute(Runnable command) {
 			command.run(); // Just run the command is the same thread
 		}
 	
 	}
 	
 			if(this.<=0) {
 			} else {
 			}
 		}
 	}

Executor used to optimise the ReinviteSender Runnable in the sendRequest of the SipDialog
 
 	private ExecutorService reinviteExecutor = Executors
 				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)
 	class PingTimer extends SIPStackTimerTask {
 		// / Timer thread handle
 
 		// / Constructor
 		public PingTimer(ThreadAuditor.ThreadHandle a_oThreadHandle) {
 			 = a_oThreadHandle;
 		}
 
 		protected 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
 				}
 
 				// Let the thread auditor know that the timer task is alive
 
 				// Schedule the next ping
 			}
 		}
 
 	}
 
 
 
 				SIPClientTransaction sipClientTransaction) {
 			this. = sipClientTransaction;
 		}
 
 		protected void runTask() {
 		}
 
 	}
 
 	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.
 		// Handle IO for this process.
 		this. = new IOHandler(this);
 
 		// The read time out is infinite.
 		this. = -1;
 
 
 		// The default (identity) address lookup scheme
 
 
 		// Notify may or may not create a dialog. This is handled in
 		// the code.
 		// Create the transaction collections
 
 		// Dialog dable.
 
 
 		// Start the timer event thread.
 
 		this. = new Timer();
 
 
 			// Start monitoring the timer thread
 			.schedule(new PingTimer(null), 0);
 		}
 	}

Re Initialize the stack instance.
 
 	protected void reInit() {
 			.logDebug("Re-initializing !");
 
 		// Array of message processors.
 		// Handle IO for this process.
 		this. = new IOHandler(this);
 		// clientTransactions = new ConcurrentLinkedQueue();
 		// serverTransactions = new ConcurrentLinkedQueue();
 		// Dialog dable.
 
 		this. = new Timer();
 
 
 	}

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
 
 	public SocketAddress obtainLocalAddress(InetAddress dstint dstPort,
 			InetAddress localAddressint localPortthrows IOException {
 		return this..obtainLocalAddress(dstdstPortlocalAddress,
 				localPort);
 
 	}

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

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

Print the dialog table.
 
 	public void printDialogTable() {
 					"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) {
 	}

Add an extension method.

Parameters:
extensionMethod -- extension method to support for dialog creation
 
 	public void addExtensionMethod(String extensionMethod) {
 		if (extensionMethod.equals(.)) {
 				.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 void putDialog(SIPDialog dialog) {
 		String dialogId = dialog.getDialogId();
 		if (.containsKey(dialogId)) {
 						.logDebug("putDialog: dialog already exists" + dialogId
 								+ " in table = " + .get(dialogId));
 			}
 			return;
 		}
 			.logDebug("putDialog dialogId=" + dialogId
 					+ " dialog = " + dialog);
 		}
 		dialog.setStack(this);
 		.put(dialogIddialog);
 		putMergeDialog(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 (this..get(dialogId) != null) {
 				SIPDialog dialog = this..get(dialogId);
 				if (dialog.getState() == null
 						|| dialog.getState() == .) {
 					retval = dialog;
 				} else {
 					retval = new SIPDialog(transaction);
 					this..put(dialogIdretval);
 				}
 			} else {
 				retval = new SIPDialog(transaction);
 				this..put(dialogIdretval);
 			}
 		} else {
 			retval = new SIPDialog(transaction);
 		}
 
 		return retval;
 
 	}

Create a Dialog given a client tx and response.

Parameters:
transaction
sipResponse
Returns:
 
 
 			SIPResponse sipResponse) {
 		String dialogId = ((SIPRequesttransaction.getRequest())
 				.getDialogId(false);
 		SIPDialog retval = null;
 		if (this..get(dialogId) != null) {
 			retval = this..get(dialogId);
 			if (sipResponse.isFinalResponse()) {
 				this..remove(dialogId);
 			}
 
 		} else {
 			retval = new SIPDialog(transactionsipResponse);
 		}
 		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);
 		}
 
 
 		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.
 
 						.getSipProvider(), dialog);
 
 				// Provide notification to the listener that the dialog has
 				// ended.
 				dialog.getSipProvider().handleEvent(eventnull);
 
 			}
 
 						.getSipProvider(), dialog);
 
 				// Provide notification to the listener that the dialog has
 				// ended.
 				dialog.getSipProvider().handleEvent(eventnull);
 
 			}
 		}
 
 	}
 
 	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);
 			.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) {
 			.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 {
 				.logDebug("ct table size = "
 			String thisToTag = notifyMessage.getTo().getTag();
 			if (thisToTag == null) {
 				return retval;
 			}
 			Event eventHdr = (EventnotifyMessage.getHeader(.);
 			if (eventHdr == null) {
 							.logDebug("event Header is null -- returning null");
 				}
 
 				return retval;
 			}
 			while (it.hasNext()) {
 					continue;
 
 				// if ( sipProvider.getListeningPoint(transport) == null)
 				String fromTag = ct.from.getTag();
 				Event hisEvent = ct.event;
 				// Event header is mandatory but some slopply clients
 				// dont include it.
 				if (hisEvent == null)
 					continue;
 					.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.callId.getCallId())) {
 					if (!this.isDeliverUnsolicitedNotify()) {
 						ct.acquireSem();
 					}
 					retval = ct;
 					return ct;
 				}
 			}
 
 			return retval;
 		} finally {
 				.logDebug("findSubscribeTransaction : returning "
 						+ retval);
 
 		}
 
 	}

Add entry to "Transaction Pending ACK" table.

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

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

Parameters:
ackMessage
Returns:
	}

Remove entry from "Transaction Pending ACK" table.

Parameters:
serverTransaction
Returns:
			SIPServerTransaction serverTransaction) {
		String branchId = ((SIPRequestserverTransaction.getRequest())
		if (branchId != null
						.containsKey(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())
	}

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.
			boolean isServer) {
		SIPTransaction retval = null;
		try {
			if (isServer) {
				Via via = sipMessage.getTopmostVia();
				if (via.getBranch() != null) {
					String key = sipMessage.getTransactionId();
										"serverTx: looking for key " + key
" existing="
					if (key
						return retval;
				// Need to scan the table for old style transactions (RFC 2543
				// style)
				while (it.hasNext()) {
					SIPServerTransaction sipServerTransaction = (SIPServerTransactionit
					if (sipServerTransaction
						retval = sipServerTransaction;
						return retval;
else {
				Via via = sipMessage.getTopmostVia();
				if (via.getBranch() != null) {
					String key = sipMessage.getTransactionId();
								"clientTx: looking for key " + key);
					if (key
						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.
				while (it.hasNext()) {
					SIPClientTransaction clientTransaction = (SIPClientTransactionit
					if (clientTransaction
						retval = clientTransaction;
						return retval;
finally {
						"findTransaction: returning  : " + retval);
		return retval;
	}

Get the transaction to cancel. Search the server transaction table for a transaction that matches the given transaction.
			boolean isServer) {
			.logDebug("findCancelTransaction request= \n"
cancelRequest + "\nfindCancelRequest isServer="
isServer);
		if (isServer) {
			while (li.hasNext()) {
				SIPTransaction transaction = (SIPTransactionli.next();
				SIPServerTransaction sipServerTransaction = (SIPServerTransactiontransaction;
				if (sipServerTransaction
					return sipServerTransaction;
else {
			while (li.hasNext()) {
				SIPTransaction transaction = (SIPTransactionli.next();
				SIPClientTransaction sipClientTransaction = (SIPClientTransactiontransaction;
				if (sipClientTransaction
					return sipClientTransaction;
					.logDebug("Could not find transaction for cancel request");
		return null;
	}

Construcor for the stack. Registers the request and response factories for the stack.

Parameters:
messageFactory User-implemented factory for processing messages.
	protected SIPTransactionStack(StackMessageFactory messageFactory) {
		this();
		this. = messageFactory;
	}

Finds a pending server transaction. Since each request may be handled either statefully or statelessly, we keep a map of pending transactions so that a duplicate transaction is not created if a second request is recieved while the first one is being processed.

Parameters:
requestReceived
Returns:
-- the pending transaction or null if no such transaction exists.
			SIPRequest requestReceived) {
			this..logDebug("looking for pending tx for :"
requestReceived.getTransactionId());
	}

See if there is a pending transaction with the same Merge ID as the Merge ID obtained from the SIP Request. The Merge table is for handling the following condition: If the request has no tag in the To header field, the UAS core MUST check the request against ongoing transactions. If the From tag, Call-ID, and CSeq exactly match those associated with an ongoing transaction, but the request does not match that transaction (based on the matching rules in Section 17.2.3), the UAS core SHOULD generate a 482 (Loop Detected) response and pass it to the server transaction.
	public boolean findMergedTransaction(SIPRequest sipRequest) {
		if (!sipRequest.getMethod().equals(.)) {
			/*
			 * Dont need to worry about request merging for Non-INVITE
			 * transactions.
			 */
			return false;
		String mergeId = sipRequest.getMergeId();
		if (mergeId != null) {
					.get(mergeId);
			if (mergedTransaction != null
					&& !mergedTransaction
				return true;
			}else {
				/*
				 * Check for loop detection for really late arriving
				 * requests
				 */
						.get(mergeId);
				if (serverDialog != null && serverDialog.firstTransactionIsServerTransaction
						&& serverDialog.getState() == .) {
					return true;
		return false;
	}

Remove a pending Server transaction from the stack. This is called after the user code has completed execution in the listener.

Parameters:
tr -- pending transaction to remove.
			this..logDebug("removePendingTx: "
	}

Remove a transaction from the merge table.

Parameters:
tr -- the server transaction to remove from the merge table.
			this..logDebug("Removing tx from merge table ");
		if (key != null) {
	}

Put this into the merge request table.

Parameters:
sipTransaction -- transaction to put into the merge table.
	public void putInMergeTable(SIPServerTransaction sipTransaction,
			SIPRequest sipRequest) {
		String mergeKey = sipRequest.getMergeId();
		if (mergeKey != null) {
			this..put(mergeKeysipTransaction);
	}

Map a Server transaction (possibly sending out a 100 if the server tx is an INVITE). This actually places it in the hash table and makes it known to the stack.

Parameters:
transaction -- the server transaction to map.
	public void mapTransaction(SIPServerTransaction transaction) {
		if (transaction.isMapped)
			return;
		addTransactionHash(transaction);
		// transaction.startTransactionTimer();
		transaction.isMapped = true;
	}

Handles a new SIP request. It finds a server transaction to handle this message. If none exists, it creates a new transaction.

Parameters:
requestReceived Request to handle.
requestMessageChannel Channel that received message.
Returns:
A server transaction.
			SIPRequest requestReceivedMessageChannel requestMessageChannel) {
		// Iterator through all server transactions
		Iterator<SIPServerTransactiontransactionIterator;
		// Next transaction in the set
		SIPServerTransaction nextTransaction;
		// Transaction to handle this request
		SIPServerTransaction currentTransaction;
		String key = requestReceived.getTransactionId();
		requestReceived.setMessageChannel(requestMessageChannel);
				.get(key);
		// Got to do this for bacasswards compatibility.
		if (currentTransaction == null
				|| !currentTransaction
						.isMessagePartOfTransaction(requestReceived)) {
			// Loop through all server transactions
			transactionIterator = .values().iterator();
			currentTransaction = null;
				while (transactionIterator.hasNext()
						&& currentTransaction == null) {
					nextTransaction = (SIPServerTransactiontransactionIterator
					// If this transaction should handle this request,
					if (nextTransaction
							.isMessagePartOfTransaction(requestReceived)) {
						// Mark this transaction as the one