1/*
2 * Conditions Of Use
3 *
4 * This software was developed by employees of the National Institute of
5 * Standards and Technology (NIST), an agency of the Federal Government.
6 * Pursuant to title 15 Untied States Code Section 105, works of NIST
7 * employees are not subject to copyright protection in the United States
8 * and are considered to be in the public domain.  As a result, a formal
9 * license is not needed to use the software.
10 *
11 * This software is provided by NIST as a service and is expressly
12 * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
13 * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
14 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
15 * AND DATA ACCURACY.  NIST does not warrant or make any representations
16 * regarding the use of the software or the results thereof, including but
17 * not limited to the correctness, accuracy, reliability or usefulness of
18 * the software.
19 *
20 * Permission to use this software is contingent upon your acceptance
21 * of the terms of this agreement
22 *
23 * .
24 *
25 */
26package gov.nist.javax.sip.stack;
27
28import gov.nist.core.Host;
29import gov.nist.core.HostPort;
30import gov.nist.core.ServerLogger;
31import gov.nist.core.StackLogger;
32import gov.nist.core.ThreadAuditor;
33import gov.nist.core.net.AddressResolver;
34import gov.nist.core.net.DefaultNetworkLayer;
35import gov.nist.core.net.NetworkLayer;
36import gov.nist.javax.sip.DefaultAddressResolver;
37import gov.nist.javax.sip.ListeningPointImpl;
38import gov.nist.javax.sip.LogRecordFactory;
39import gov.nist.javax.sip.SIPConstants;
40import gov.nist.javax.sip.SipListenerExt;
41import gov.nist.javax.sip.SipProviderImpl;
42import gov.nist.javax.sip.SipStackImpl;
43import gov.nist.javax.sip.header.Event;
44import gov.nist.javax.sip.header.Via;
45import gov.nist.javax.sip.header.extensions.JoinHeader;
46import gov.nist.javax.sip.header.extensions.ReplacesHeader;
47import gov.nist.javax.sip.message.SIPMessage;
48import gov.nist.javax.sip.message.SIPRequest;
49import gov.nist.javax.sip.message.SIPResponse;
50
51import java.io.IOException;
52import java.net.InetAddress;
53import java.net.SocketAddress;
54import java.net.UnknownHostException;
55import java.util.ArrayList;
56import java.util.Collection;
57import java.util.HashSet;
58import java.util.Iterator;
59import java.util.LinkedList;
60import java.util.Set;
61import java.util.Timer;
62import java.util.concurrent.ConcurrentHashMap;
63import java.util.concurrent.atomic.AtomicInteger;
64
65import javax.sip.ClientTransaction;
66import javax.sip.Dialog;
67import javax.sip.DialogState;
68import javax.sip.DialogTerminatedEvent;
69import javax.sip.ServerTransaction;
70import javax.sip.SipException;
71import javax.sip.SipListener;
72import javax.sip.TransactionState;
73import javax.sip.TransactionTerminatedEvent;
74import javax.sip.address.Hop;
75import javax.sip.address.Router;
76import javax.sip.header.CallIdHeader;
77import javax.sip.header.EventHeader;
78import javax.sip.message.Request;
79import javax.sip.message.Response;
80
81/*
82 * Jeff Keyser : architectural suggestions and contributions. Pierre De Rop and Thomas Froment :
83 * Bug reports. Jeyashankher < jai@lucent.com > : bug reports. Jeroen van Bemmel : Bug fixes.
84 *
85 *
86 */
87
88/**
89 *
90 * This is the sip stack. It is essentially a management interface. It manages the resources for
91 * the JAIN-SIP implementation. This is the structure that is wrapped by the SipStackImpl.
92 *
93 * @see gov.nist.javax.sip.SipStackImpl
94 *
95 * @author M. Ranganathan <br/>
96 *
97 * @version 1.2 $Revision: 1.141 $ $Date: 2009/12/17 23:38:27 $
98 */
99public abstract class SIPTransactionStack implements SIPTransactionEventListener, SIPDialogEventListener {
100
101    /*
102     * Number of milliseconds between timer ticks (500).
103     */
104    public static final int BASE_TIMER_INTERVAL = 500;
105
106    /*
107     * Connection linger time (seconds) this is the time (in seconds) for which we linger the TCP
108     * connection before closing it.
109     */
110    public static final int CONNECTION_LINGER_TIME = 8;
111
112    /*
113     * Table of retransmission Alert timers.
114     */
115    protected ConcurrentHashMap<String, SIPServerTransaction> retransmissionAlertTransactions;
116
117    // Table of early dialogs ( to keep identity mapping )
118    protected ConcurrentHashMap<String, SIPDialog> earlyDialogTable;
119
120    // Table of dialogs.
121    protected ConcurrentHashMap<String, SIPDialog> dialogTable;
122
123    // A set of methods that result in dialog creations.
124    protected static final Set<String> dialogCreatingMethods = new HashSet<String>();
125
126    // Global timer. Use this for all timer tasks.
127
128    private Timer timer;
129
130    // List of pending server transactions
131    private ConcurrentHashMap<String, SIPServerTransaction> pendingTransactions;
132
133    // hashtable for fast lookup
134    private ConcurrentHashMap<String, SIPClientTransaction> clientTransactionTable;
135
136    // Set to false if you want hiwat and lowat to be consulted.
137    protected boolean unlimitedServerTransactionTableSize = true;
138
139    // Set to false if you want unlimited size of client trnansactin table.
140    protected boolean unlimitedClientTransactionTableSize = true;
141
142    // High water mark for ServerTransaction Table
143    // after which requests are dropped.
144    protected int serverTransactionTableHighwaterMark = 5000;
145
146    // Low water mark for Server Tx table size after which
147    // requests are selectively dropped
148    protected int serverTransactionTableLowaterMark = 4000;
149
150    // Hiwater mark for client transaction table. These defaults can be
151    // overriden by stack
152    // configuration.
153    protected int clientTransactionTableHiwaterMark = 1000;
154
155    // Low water mark for client tx table.
156    protected int clientTransactionTableLowaterMark = 800;
157
158    private AtomicInteger activeClientTransactionCount = new AtomicInteger(0);
159
160    // Hashtable for server transactions.
161    private ConcurrentHashMap<String, SIPServerTransaction> serverTransactionTable;
162
163    // A table of ongoing transactions indexed by mergeId ( for detecting merged
164    // requests.
165    private ConcurrentHashMap<String, SIPServerTransaction> mergeTable;
166
167    private ConcurrentHashMap<String,SIPServerTransaction> terminatedServerTransactionsPendingAck;
168
169    private ConcurrentHashMap<String,SIPClientTransaction> forkedClientTransactionTable;
170
171    /*
172     * A wrapper around differnt logging implementations (log4j, commons logging, slf4j, ...) to help log debug.
173     */
174    private StackLogger stackLogger;
175
176    /*
177     * ServerLog is used just for logging stack message tracecs.
178     */
179    protected ServerLogger serverLogger;
180
181    /*
182     * We support UDP on this stack.
183     */
184    boolean udpFlag;
185
186    /*
187     * Internal router. Use this for all sip: request routing.
188     *
189     */
190    protected DefaultRouter defaultRouter;
191
192    /*
193     * Global flag that turns logging off
194     */
195    protected boolean needsLogging;
196
197    /*
198     * Flag used for testing TI, bypasses filtering of ACK to non-2xx
199     */
200    private boolean non2XXAckPassedToListener;
201
202    /*
203     * Class that handles caching of TCP/TLS connections.
204     */
205    protected IOHandler ioHandler;
206
207    /*
208     * Flag that indicates that the stack is active.
209     */
210    protected boolean toExit;
211
212    /*
213     * Name of the stack.
214     */
215    protected String stackName;
216
217    /*
218     * IP address of stack -- this can be re-written by stun.
219     *
220     * @deprecated
221     */
222    protected String stackAddress;
223
224    /*
225     * INET address of stack (cached to avoid repeated lookup)
226     *
227     * @deprecated
228     */
229    protected InetAddress stackInetAddress;
230
231    /*
232     * Request factory interface (to be provided by the application)
233     */
234    protected StackMessageFactory sipMessageFactory;
235
236    /*
237     * Router to determine where to forward the request.
238     */
239    protected javax.sip.address.Router router;
240
241    /*
242     * Number of pre-allocated threads for processing udp messages. -1 means no preallocated
243     * threads ( dynamically allocated threads).
244     */
245    protected int threadPoolSize;
246
247    /*
248     * max number of simultaneous connections.
249     */
250    protected int maxConnections;
251
252    /*
253     * Close accept socket on completion.
254     */
255    protected boolean cacheServerConnections;
256
257    /*
258     * Close connect socket on Tx termination.
259     */
260    protected boolean cacheClientConnections;
261
262    /*
263     * Use the user supplied router for all out of dialog requests.
264     */
265    protected boolean useRouterForAll;
266
267    /*
268     * Max size of message that can be read from a TCP connection.
269     */
270    protected int maxContentLength;
271
272    /*
273     * Max # of headers that a SIP message can contain.
274     */
275    protected int maxMessageSize;
276
277    /*
278     * A collection of message processors.
279     */
280    private Collection<MessageProcessor> messageProcessors;
281
282    /*
283     * Read timeout on TCP incoming sockets -- defines the time between reads for after delivery
284     * of first byte of message.
285     */
286    protected int readTimeout;
287
288    /*
289     * The socket factory. Can be overriden by applications that want direct access to the
290     * underlying socket.
291     */
292
293    protected NetworkLayer networkLayer;
294
295    /*
296     * Outbound proxy String ( to be handed to the outbound proxy class on creation).
297     */
298    protected String outboundProxy;
299
300    protected String routerPath;
301
302    // Flag to indicate whether the stack will provide dialog
303    // support.
304    protected boolean isAutomaticDialogSupportEnabled;
305
306    // The set of events for which subscriptions can be forked.
307
308    protected HashSet<String> forkedEvents;
309
310    // Generate a timestamp header for retransmitted requests.
311    protected boolean generateTimeStampHeader;
312
313    protected AddressResolver addressResolver;
314
315    // Max time that the listener is allowed to take to respond to a
316    // request. Default is "infinity". This property allows
317    // containers to defend against buggy clients (that do not
318    // want to respond to requests).
319    protected int maxListenerResponseTime;
320
321
322    // A flag that indicates whether or not RFC 2543 clients are fully supported.
323    // If this is set to true, then To tag checking on the Dialog layer is
324    // disabled in a few places - resulting in possible breakage of forked dialogs.
325    protected boolean rfc2543Supported = true;
326
327    // / Provides a mechanism for applications to check the health of threads in
328    // the stack
329    protected ThreadAuditor threadAuditor = new ThreadAuditor();
330
331    protected LogRecordFactory logRecordFactory;
332
333    // Set to true if the client CANCEL transaction should be checked before sending
334    // it out.
335    protected boolean cancelClientTransactionChecked = true;
336
337    // Is to tag reassignment allowed.
338    protected boolean remoteTagReassignmentAllowed = true;
339
340    protected boolean logStackTraceOnMessageSend = true;
341
342    // Receive UDP buffer size
343    protected int receiveUdpBufferSize;
344
345    // Send UDP buffer size
346    protected int sendUdpBufferSize;
347
348    protected boolean stackDoesCongestionControl = true;
349
350    protected boolean isBackToBackUserAgent = false;
351
352    protected boolean checkBranchId;
353
354	protected boolean isAutomaticDialogErrorHandlingEnabled = true;
355
356	protected boolean isDialogTerminatedEventDeliveredForNullDialog = false;
357
358	// Max time for a forked response to arrive. After this time, the original dialog
359	// is not tracked. If you want to track the original transaction you need to specify
360	// the max fork time with a stack init property.
361	protected int maxForkTime = 0;
362
363
364    // / Timer to regularly ping the thread auditor (on behalf of the timer
365    // thread)
366    class PingTimer extends SIPStackTimerTask {
367        // / Timer thread handle
368        ThreadAuditor.ThreadHandle threadHandle;
369
370        // / Constructor
371        public PingTimer(ThreadAuditor.ThreadHandle a_oThreadHandle) {
372            threadHandle = a_oThreadHandle;
373        }
374
375        protected void runTask() {
376            // Check if we still have a timer (it may be null after shutdown)
377            if (getTimer() != null) {
378                // Register the timer task if we haven't done so
379                if (threadHandle == null) {
380                    // This happens only once since the thread handle is passed
381                    // to the next scheduled ping timer
382                    threadHandle = getThreadAuditor().addCurrentThread();
383                }
384
385                // Let the thread auditor know that the timer task is alive
386                threadHandle.ping();
387
388                // Schedule the next ping
389                getTimer().schedule(new PingTimer(threadHandle),
390                        threadHandle.getPingIntervalInMillisecs());
391            }
392        }
393
394    }
395
396
397    class RemoveForkedTransactionTimerTask extends SIPStackTimerTask {
398
399        private SIPClientTransaction clientTransaction;
400
401        public RemoveForkedTransactionTimerTask(SIPClientTransaction sipClientTransaction ) {
402            this.clientTransaction = sipClientTransaction;
403        }
404
405        @Override
406        protected void runTask() {
407           forkedClientTransactionTable.remove(clientTransaction.getTransactionId());
408        }
409
410    }
411
412    static {
413    	// Standard set of methods that create dialogs.
414    	dialogCreatingMethods.add(Request.REFER);
415        dialogCreatingMethods.add(Request.INVITE);
416        dialogCreatingMethods.add(Request.SUBSCRIBE);
417    }
418
419    /**
420     * Default constructor.
421     */
422    protected SIPTransactionStack() {
423        this.toExit = false;
424        this.forkedEvents = new HashSet<String>();
425        // set of events for which subscriptions can be forked.
426        // Set an infinite thread pool size.
427        this.threadPoolSize = -1;
428        // Close response socket after infinte time.
429        // for max performance
430        this.cacheServerConnections = true;
431        // Close the request socket after infinite time.
432        // for max performance
433        this.cacheClientConnections = true;
434        // Max number of simultaneous connections.
435        this.maxConnections = -1;
436        // Array of message processors.
437        messageProcessors = new ArrayList<MessageProcessor>();
438        // Handle IO for this process.
439        this.ioHandler = new IOHandler(this);
440
441        // The read time out is infinite.
442        this.readTimeout = -1;
443
444        this.maxListenerResponseTime = -1;
445
446        // The default (identity) address lookup scheme
447
448        this.addressResolver = new DefaultAddressResolver();
449
450        // Notify may or may not create a dialog. This is handled in
451        // the code.
452        // Create the transaction collections
453
454        // Dialog dable.
455        this.dialogTable = new ConcurrentHashMap<String, SIPDialog>();
456        this.earlyDialogTable = new ConcurrentHashMap<String, SIPDialog>();
457
458        clientTransactionTable = new ConcurrentHashMap<String, SIPClientTransaction>();
459        serverTransactionTable = new ConcurrentHashMap<String, SIPServerTransaction>();
460        this.terminatedServerTransactionsPendingAck = new ConcurrentHashMap<String, SIPServerTransaction>();
461        mergeTable = new ConcurrentHashMap<String, SIPServerTransaction>();
462        retransmissionAlertTransactions = new ConcurrentHashMap<String, SIPServerTransaction>();
463
464        // Start the timer event thread.
465
466        this.timer = new Timer();
467        this.pendingTransactions = new ConcurrentHashMap<String, SIPServerTransaction>();
468
469
470        this.forkedClientTransactionTable = new ConcurrentHashMap<String,SIPClientTransaction>();
471
472        if (getThreadAuditor().isEnabled()) {
473            // Start monitoring the timer thread
474            timer.schedule(new PingTimer(null), 0);
475        }
476    }
477
478    /**
479     * Re Initialize the stack instance.
480     */
481    protected void reInit() {
482        if (stackLogger.isLoggingEnabled())
483            stackLogger.logDebug("Re-initializing !");
484
485        // Array of message processors.
486        messageProcessors = new ArrayList<MessageProcessor>();
487        // Handle IO for this process.
488        this.ioHandler = new IOHandler(this);
489        // clientTransactions = new ConcurrentLinkedQueue();
490        // serverTransactions = new ConcurrentLinkedQueue();
491        pendingTransactions = new ConcurrentHashMap<String, SIPServerTransaction>();
492        clientTransactionTable = new ConcurrentHashMap<String, SIPClientTransaction>();
493        serverTransactionTable = new ConcurrentHashMap<String, SIPServerTransaction>();
494        retransmissionAlertTransactions = new ConcurrentHashMap<String, SIPServerTransaction>();
495        mergeTable = new ConcurrentHashMap<String, SIPServerTransaction>();
496        // Dialog dable.
497        this.dialogTable = new ConcurrentHashMap<String, SIPDialog>();
498        this.earlyDialogTable = new ConcurrentHashMap<String, SIPDialog>();
499        this.terminatedServerTransactionsPendingAck = new ConcurrentHashMap<String,SIPServerTransaction>();
500        this.forkedClientTransactionTable = new ConcurrentHashMap<String,SIPClientTransaction>();
501
502        this.timer = new Timer();
503
504        this.activeClientTransactionCount = new AtomicInteger(0);
505
506    }
507
508    /**
509     * Creates and binds, if necessary, a socket connected to the specified
510     * destination address and port and then returns its local address.
511     *
512     * @param dst the destination address that the socket would need to connect
513     *            to.
514     * @param dstPort the port number that the connection would be established
515     * with.
516     * @param localAddress the address that we would like to bind on
517     * (null for the "any" address).
518     * @param localPort the port that we'd like our socket to bind to (0 for a
519     * random port).
520     *
521     * @return the SocketAddress that this handler would use when connecting to
522     * the specified destination address and port.
523     *
524     * @throws IOException
525     */
526    public SocketAddress obtainLocalAddress(InetAddress dst, int dstPort,
527                    InetAddress localAddress, int localPort)
528        throws IOException
529    {
530        return this.ioHandler.obtainLocalAddress(
531                        dst, dstPort, localAddress, localPort);
532
533    }
534
535    /**
536     * For debugging -- allows you to disable logging or enable logging selectively.
537     *
538     *
539     */
540    public void disableLogging() {
541        this.getStackLogger().disableLogging();
542    }
543
544    /**
545     * Globally enable message logging ( for debugging)
546     *
547     */
548    public void enableLogging() {
549        this.getStackLogger().enableLogging();
550    }
551
552    /**
553     * Print the dialog table.
554     *
555     */
556    public void printDialogTable() {
557        if (isLoggingEnabled()) {
558            this.getStackLogger().logDebug("dialog table  = " + this.dialogTable);
559            System.out.println("dialog table = " + this.dialogTable);
560        }
561    }
562
563    /**
564     * Retrieve a transaction from our table of transactions with pending retransmission alerts.
565     *
566     * @param dialogId
567     * @return -- the RetransmissionAlert enabled transaction corresponding to the given dialog
568     *         ID.
569     */
570    public SIPServerTransaction getRetransmissionAlertTransaction(String dialogId) {
571        return (SIPServerTransaction) this.retransmissionAlertTransactions.get(dialogId);
572    }
573
574    /**
575     * Return true if extension is supported.
576     *
577     * @return true if extension is supported and false otherwise.
578     */
579    public static boolean isDialogCreated(String method) {
580    	return dialogCreatingMethods.contains(method);
581    }
582
583    /**
584     * Add an extension method.
585     *
586     * @param extensionMethod -- extension method to support for dialog creation
587     */
588    public void addExtensionMethod(String extensionMethod) {
589        if (extensionMethod.equals(Request.NOTIFY)) {
590            if (stackLogger.isLoggingEnabled())
591                stackLogger.logDebug("NOTIFY Supported Natively");
592        } else {
593            dialogCreatingMethods.add(extensionMethod.trim().toUpperCase());
594        }
595    }
596
597    /**
598     * Put a dialog into the dialog table.
599     *
600     * @param dialog -- dialog to put into the dialog table.
601     *
602     */
603    public void putDialog(SIPDialog dialog) {
604        String dialogId = dialog.getDialogId();
605        if (dialogTable.containsKey(dialogId)) {
606            if (stackLogger.isLoggingEnabled()) {
607                stackLogger.logDebug("putDialog: dialog already exists" + dialogId + " in table = "
608                        + dialogTable.get(dialogId));
609            }
610            return;
611        }
612        if (stackLogger.isLoggingEnabled()) {
613            stackLogger.logDebug("putDialog dialogId=" + dialogId + " dialog = " + dialog);
614        }
615        dialog.setStack(this);
616        if (stackLogger.isLoggingEnabled())
617            stackLogger.logStackTrace();
618        dialogTable.put(dialogId, dialog);
619
620    }
621
622    /**
623     * Create a dialog and add this transaction to it.
624     *
625     * @param transaction -- tx to add to the dialog.
626     * @return the newly created Dialog.
627     */
628    public SIPDialog createDialog(SIPTransaction transaction) {
629
630        SIPDialog retval = null;
631
632        if (transaction instanceof SIPClientTransaction) {
633            String dialogId = ((SIPRequest) transaction.getRequest()).getDialogId(false);
634            if (this.earlyDialogTable.get(dialogId) != null) {
635                SIPDialog dialog = this.earlyDialogTable.get(dialogId);
636                if (dialog.getState() == null || dialog.getState() == DialogState.EARLY) {
637                    retval = dialog;
638                } else {
639                    retval = new SIPDialog(transaction);
640                    this.earlyDialogTable.put(dialogId, retval);
641                }
642            } else {
643                retval = new SIPDialog(transaction);
644                this.earlyDialogTable.put(dialogId, retval);
645            }
646        } else {
647            retval = new SIPDialog(transaction);
648        }
649
650        return retval;
651
652    }
653
654    /**
655     * Create a Dialog given a client tx and response.
656     *
657     * @param transaction
658     * @param sipResponse
659     * @return
660     */
661
662    public SIPDialog createDialog(SIPClientTransaction transaction, SIPResponse sipResponse) {
663        String dialogId = ((SIPRequest) transaction.getRequest()).getDialogId(false);
664        SIPDialog retval = null;
665        if (this.earlyDialogTable.get(dialogId) != null) {
666            retval = this.earlyDialogTable.get(dialogId);
667            if (sipResponse.isFinalResponse()) {
668                this.earlyDialogTable.remove(dialogId);
669            }
670
671        } else {
672            retval = new SIPDialog(transaction, sipResponse);
673        }
674        return retval;
675
676    }
677    /**
678     * Create a Dialog given a sip provider and response.
679     *
680     * @param sipProvider
681     * @param sipResponse
682     * @return
683     */
684    public SIPDialog createDialog(SipProviderImpl sipProvider,
685			SIPResponse sipResponse) {
686		return new SIPDialog(sipProvider, sipResponse);
687	}
688
689    /**
690     * Remove the dialog from the dialog table.
691     *
692     * @param dialog -- dialog to remove.
693     */
694    public void removeDialog(SIPDialog dialog) {
695
696        String id = dialog.getDialogId();
697
698        String earlyId = dialog.getEarlyDialogId();
699
700        if (earlyId != null) {
701            this.earlyDialogTable.remove(earlyId);
702            this.dialogTable.remove(earlyId);
703        }
704
705        if (id != null) {
706
707            // FHT: Remove dialog from table only if its associated dialog is the same as the one
708            // specified
709
710            Object old = this.dialogTable.get(id);
711
712            if (old == dialog) {
713                this.dialogTable.remove(id);
714            }
715
716            // We now deliver DTE even when the dialog is not originally present in the Dialog
717            // Table
718            // This happens before the dialog state is assigned.
719
720            if (!dialog.testAndSetIsDialogTerminatedEventDelivered()) {
721                DialogTerminatedEvent event = new DialogTerminatedEvent(dialog.getSipProvider(),
722                        dialog);
723
724                // Provide notification to the listener that the dialog has
725                // ended.
726                dialog.getSipProvider().handleEvent(event, null);
727
728            }
729
730        } else if ( this.isDialogTerminatedEventDeliveredForNullDialog ) {
731            if (!dialog.testAndSetIsDialogTerminatedEventDelivered()) {
732                DialogTerminatedEvent event = new DialogTerminatedEvent(dialog.getSipProvider(),
733                        dialog);
734
735                // Provide notification to the listener that the dialog has
736                // ended.
737                dialog.getSipProvider().handleEvent(event, null);
738
739            }
740        }
741
742    }
743
744    /**
745     * Return the dialog for a given dialog ID. If compatibility is enabled then we do not assume
746     * the presence of tags and hence need to add a flag to indicate whether this is a server or
747     * client transaction.
748     *
749     * @param dialogId is the dialog id to check.
750     */
751
752    public SIPDialog getDialog(String dialogId) {
753
754        SIPDialog sipDialog = (SIPDialog) dialogTable.get(dialogId);
755        if (stackLogger.isLoggingEnabled()) {
756            stackLogger.logDebug("getDialog(" + dialogId + ") : returning " + sipDialog);
757        }
758        return sipDialog;
759
760    }
761
762    /**
763     * Remove the dialog given its dialog id. This is used for dialog id re-assignment only.
764     *
765     * @param dialogId is the dialog Id to remove.
766     */
767    public void removeDialog(String dialogId) {
768        if (stackLogger.isLoggingEnabled()) {
769            stackLogger.logWarning("Silently removing dialog from table");
770        }
771        dialogTable.remove(dialogId);
772    }
773
774    /**
775     * Find a matching client SUBSCRIBE to the incoming notify. NOTIFY requests are matched to
776     * such SUBSCRIBE requests if they contain the same "Call-ID", a "To" header "tag" parameter
777     * which matches the "From" header "tag" parameter of the SUBSCRIBE, and the same "Event"
778     * header field. Rules for comparisons of the "Event" headers are described in section 7.2.1.
779     * If a matching NOTIFY request contains a "Subscription-State" of "active" or "pending", it
780     * creates a new subscription and a new dialog (unless they have already been created by a
781     * matching response, as described above).
782     *
783     * @param notifyMessage
784     * @return -- the matching ClientTransaction with semaphore aquired or null if no such client
785     *         transaction can be found.
786     */
787    public SIPClientTransaction findSubscribeTransaction(SIPRequest notifyMessage,
788            ListeningPointImpl listeningPoint) {
789        SIPClientTransaction retval = null;
790        try {
791            Iterator it = clientTransactionTable.values().iterator();
792            if (stackLogger.isLoggingEnabled())
793            	stackLogger.logDebug("ct table size = " + clientTransactionTable.size());
794            String thisToTag = notifyMessage.getTo().getTag();
795            if (thisToTag == null) {
796                return retval;
797            }
798            Event eventHdr = (Event) notifyMessage.getHeader(EventHeader.NAME);
799            if (eventHdr == null) {
800                if (stackLogger.isLoggingEnabled()) {
801                    stackLogger.logDebug("event Header is null -- returning null");
802                }
803
804                return retval;
805            }
806            while (it.hasNext()) {
807                SIPClientTransaction ct = (SIPClientTransaction) it.next();
808                if (!ct.getMethod().equals(Request.SUBSCRIBE))
809                    continue;
810
811                // if ( sipProvider.getListeningPoint(transport) == null)
812                String fromTag = ct.from.getTag();
813                Event hisEvent = ct.event;
814                // Event header is mandatory but some slopply clients
815                // dont include it.
816                if (hisEvent == null)
817                    continue;
818                if (stackLogger.isLoggingEnabled()) {
819                    stackLogger.logDebug("ct.fromTag = " + fromTag);
820                    stackLogger.logDebug("thisToTag = " + thisToTag);
821                    stackLogger.logDebug("hisEvent = " + hisEvent);
822                    stackLogger.logDebug("eventHdr " + eventHdr);
823                }
824
825                if (  fromTag.equalsIgnoreCase(thisToTag)
826                      && hisEvent != null
827                      && eventHdr.match(hisEvent)
828                      && notifyMessage.getCallId().getCallId().equalsIgnoreCase(
829                                ct.callId.getCallId())) {
830                    if (ct.acquireSem())
831                        retval = ct;
832                    return retval;
833                }
834            }
835
836            return retval;
837        } finally {
838        	if (stackLogger.isLoggingEnabled())
839                stackLogger.logDebug("findSubscribeTransaction : returning " + retval);
840
841        }
842
843    }
844
845    /**
846     * Add entry to "Transaction Pending ACK" table.
847     *
848     * @param serverTransaction
849     */
850    public void addTransactionPendingAck(SIPServerTransaction serverTransaction) {
851        String branchId = ((SIPRequest)serverTransaction.getRequest()).getTopmostVia().getBranch();
852        if ( branchId != null ) {
853            this.terminatedServerTransactionsPendingAck.put(branchId, serverTransaction);
854        }
855
856    }
857
858    /**
859     * Get entry in the server transaction pending ACK table corresponding to an ACK.
860     *
861     * @param ackMessage
862     * @return
863     */
864    public SIPServerTransaction findTransactionPendingAck(SIPRequest ackMessage) {
865        return this.terminatedServerTransactionsPendingAck.get(ackMessage.getTopmostVia().getBranch());
866    }
867
868    /**
869     * Remove entry from "Transaction Pending ACK" table.
870     *
871     * @param serverTransaction
872     * @return
873     */
874
875    public boolean removeTransactionPendingAck(SIPServerTransaction serverTransaction) {
876        String branchId = ((SIPRequest)serverTransaction.getRequest()).getTopmostVia().getBranch();
877        if ( branchId != null && this.terminatedServerTransactionsPendingAck.containsKey(branchId) ) {
878            this.terminatedServerTransactionsPendingAck.remove(branchId);
879            return true;
880        } else {
881            return false;
882        }
883    }
884
885    /**
886     * Check if this entry exists in the "Transaction Pending ACK" table.
887     *
888     * @param serverTransaction
889     * @return
890     */
891    public boolean isTransactionPendingAck(SIPServerTransaction serverTransaction) {
892        String branchId = ((SIPRequest)serverTransaction.getRequest()).getTopmostVia().getBranch();
893        return this.terminatedServerTransactionsPendingAck.contains(branchId);
894    }
895
896    /**
897     * Find the transaction corresponding to a given request.
898     *
899     * @param sipMessage request for which to retrieve the transaction.
900     *
901     * @param isServer search the server transaction table if true.
902     *
903     * @return the transaction object corresponding to the request or null if no such mapping
904     *         exists.
905     */
906    public SIPTransaction findTransaction(SIPMessage sipMessage, boolean isServer) {
907        SIPTransaction retval = null;
908        try {
909            if (isServer) {
910                Via via = sipMessage.getTopmostVia();
911                if (via.getBranch() != null) {
912                    String key = sipMessage.getTransactionId();
913
914                    retval = (SIPTransaction) serverTransactionTable.get(key);
915                    if (stackLogger.isLoggingEnabled())
916                        getStackLogger().logDebug(
917                                "serverTx: looking for key " + key + " existing="
918                                + serverTransactionTable);
919                    if (key.startsWith(SIPConstants.BRANCH_MAGIC_COOKIE_LOWER_CASE)) {
920                        return retval;
921                    }
922
923                }
924                // Need to scan the table for old style transactions (RFC 2543
925                // style)
926                Iterator<SIPServerTransaction> it = serverTransactionTable.values().iterator();
927                while (it.hasNext()) {
928                    SIPServerTransaction sipServerTransaction = (SIPServerTransaction) it.next();
929                    if (sipServerTransaction.isMessagePartOfTransaction(sipMessage)) {
930                        retval = sipServerTransaction;
931                        return retval;
932                    }
933                }
934
935            } else {
936                Via via = sipMessage.getTopmostVia();
937                if (via.getBranch() != null) {
938                    String key = sipMessage.getTransactionId();
939                    if (stackLogger.isLoggingEnabled())
940                        getStackLogger().logDebug("clientTx: looking for key " + key);
941                    retval = (SIPTransaction) clientTransactionTable.get(key);
942                    if (key.startsWith(SIPConstants.BRANCH_MAGIC_COOKIE_LOWER_CASE)) {
943                        return retval;
944                    }
945
946                }
947                // Need to scan the table for old style transactions (RFC 2543
948                // style). This is terribly slow but we need to do this
949                // for backasswords compatibility.
950                Iterator<SIPClientTransaction> it = clientTransactionTable.values().iterator();
951                while (it.hasNext()) {
952                    SIPClientTransaction clientTransaction = (SIPClientTransaction) it.next();
953                    if (clientTransaction.isMessagePartOfTransaction(sipMessage)) {
954                        retval = clientTransaction;
955                        return retval;
956                    }
957                }
958
959            }
960        } finally {
961        	if ( this.getStackLogger().isLoggingEnabled()) {
962        	  this.getStackLogger().logDebug("findTransaction: returning  : " + retval);
963        	}
964        }
965        return retval;
966
967    }
968
969    /**
970     * Get the transaction to cancel. Search the server transaction table for a transaction that
971     * matches the given transaction.
972     */
973    public SIPTransaction findCancelTransaction(SIPRequest cancelRequest, boolean isServer) {
974
975        if (stackLogger.isLoggingEnabled()) {
976            stackLogger.logDebug("findCancelTransaction request= \n" + cancelRequest
977                    + "\nfindCancelRequest isServer=" + isServer);
978        }
979
980        if (isServer) {
981            Iterator<SIPServerTransaction> li = this.serverTransactionTable.values().iterator();
982            while (li.hasNext()) {
983                SIPTransaction transaction = (SIPTransaction) li.next();
984
985                SIPServerTransaction sipServerTransaction = (SIPServerTransaction) transaction;
986                if (sipServerTransaction.doesCancelMatchTransaction(cancelRequest))
987                    return sipServerTransaction;
988            }
989
990        } else {
991            Iterator<SIPClientTransaction> li = this.clientTransactionTable.values().iterator();
992            while (li.hasNext()) {
993                SIPTransaction transaction = (SIPTransaction) li.next();
994
995                SIPClientTransaction sipClientTransaction = (SIPClientTransaction) transaction;
996                if (sipClientTransaction.doesCancelMatchTransaction(cancelRequest))
997                    return sipClientTransaction;
998
999            }
1000
1001        }
1002        if (stackLogger.isLoggingEnabled())
1003            stackLogger.logDebug("Could not find transaction for cancel request");
1004        return null;
1005    }
1006
1007    /**
1008     * Construcor for the stack. Registers the request and response factories for the stack.
1009     *
1010     * @param messageFactory User-implemented factory for processing messages.
1011     */
1012    protected SIPTransactionStack(StackMessageFactory messageFactory) {
1013        this();
1014        this.sipMessageFactory = messageFactory;
1015    }
1016
1017    /**
1018     * Finds a pending server transaction. Since each request may be handled either statefully or
1019     * statelessly, we keep a map of pending transactions so that a duplicate transaction is not
1020     * created if a second request is recieved while the first one is being processed.
1021     *
1022     * @param requestReceived
1023     * @return -- the pending transaction or null if no such transaction exists.
1024     */
1025    public SIPServerTransaction findPendingTransaction(SIPRequest requestReceived) {
1026        if (this.stackLogger.isLoggingEnabled()) {
1027            this.stackLogger.logDebug("looking for pending tx for :"
1028                    + requestReceived.getTransactionId());
1029        }
1030        return (SIPServerTransaction) pendingTransactions.get(requestReceived.getTransactionId());
1031
1032    }
1033
1034    /**
1035     * See if there is a pending transaction with the same Merge ID as the Merge ID obtained from
1036     * the SIP Request. The Merge table is for handling the following condition: If the request
1037     * has no tag in the To header field, the UAS core MUST check the request against ongoing
1038     * transactions. If the From tag, Call-ID, and CSeq exactly match those associated with an
1039     * ongoing transaction, but the request does not match that transaction (based on the matching
1040     * rules in Section 17.2.3), the UAS core SHOULD generate a 482 (Loop Detected) response and
1041     * pass it to the server transaction.
1042     */
1043    public SIPServerTransaction findMergedTransaction(SIPRequest sipRequest) {
1044        if (! sipRequest.getMethod().equals(Request.INVITE)) {
1045            /*
1046             * Dont need to worry about request merging for Non-INVITE transactions.
1047             */
1048            return null;
1049        }
1050        String mergeId = sipRequest.getMergeId();
1051        SIPServerTransaction mergedTransaction = (SIPServerTransaction) this.mergeTable.get(mergeId);
1052        if (mergeId == null ) {
1053            return null;
1054        } else if (mergedTransaction != null && !mergedTransaction.isMessagePartOfTransaction(sipRequest) ) {
1055            return mergedTransaction;
1056        } else {
1057            /*
1058             * Check the server transactions that have resulted in dialogs.
1059             */
1060           for (Dialog dialog: this.dialogTable.values() ) {
1061               SIPDialog sipDialog = (SIPDialog) dialog ;
1062               if (sipDialog.getFirstTransaction()  != null &&
1063                   sipDialog.getFirstTransaction() instanceof ServerTransaction) {
1064                   SIPServerTransaction serverTransaction = ((SIPServerTransaction) sipDialog.getFirstTransaction());
1065                   SIPRequest transactionRequest = ((SIPServerTransaction) sipDialog.getFirstTransaction()).getOriginalRequest();
1066                   if ( (! serverTransaction.isMessagePartOfTransaction(sipRequest))
1067                           && sipRequest.getMergeId().equals(transactionRequest.getMergeId())) {
1068                           return (SIPServerTransaction) sipDialog.getFirstTransaction();
1069                   }
1070               }
1071           }
1072           return null;
1073        }
1074    }
1075
1076    /**
1077     * Remove a pending Server transaction from the stack. This is called after the user code has
1078     * completed execution in the listener.
1079     *
1080     * @param tr -- pending transaction to remove.
1081     */
1082    public void removePendingTransaction(SIPServerTransaction tr) {
1083        if (this.stackLogger.isLoggingEnabled()) {
1084            this.stackLogger.logDebug("removePendingTx: " + tr.getTransactionId());
1085        }
1086        this.pendingTransactions.remove(tr.getTransactionId());
1087
1088    }
1089
1090    /**
1091     * Remove a transaction from the merge table.
1092     *
1093     * @param tr -- the server transaction to remove from the merge table.
1094     *
1095     */
1096    public void removeFromMergeTable(SIPServerTransaction tr) {
1097        if (stackLogger.isLoggingEnabled()) {
1098            this.stackLogger.logDebug("Removing tx from merge table ");
1099        }
1100        String key = ((SIPRequest) tr.getRequest()).getMergeId();
1101        if (key != null) {
1102            this.mergeTable.remove(key);
1103        }
1104    }
1105
1106    /**
1107     * Put this into the merge request table.
1108     *
1109     * @param sipTransaction -- transaction to put into the merge table.
1110     *
1111     */
1112    public void putInMergeTable(SIPServerTransaction sipTransaction, SIPRequest sipRequest) {
1113        String mergeKey = sipRequest.getMergeId();
1114        if (mergeKey != null) {
1115            this.mergeTable.put(mergeKey, sipTransaction);
1116        }
1117    }
1118
1119    /**
1120     * Map a Server transaction (possibly sending out a 100 if the server tx is an INVITE). This
1121     * actually places it in the hash table and makes it known to the stack.
1122     *
1123     * @param transaction -- the server transaction to map.
1124     */
1125    public void mapTransaction(SIPServerTransaction transaction) {
1126        if (transaction.isMapped)
1127            return;
1128        addTransactionHash(transaction);
1129        // transaction.startTransactionTimer();
1130        transaction.isMapped = true;
1131    }
1132
1133    /**
1134     * Handles a new SIP request. It finds a server transaction to handle this message. If none
1135     * exists, it creates a new transaction.
1136     *
1137     * @param requestReceived Request to handle.
1138     * @param requestMessageChannel Channel that received message.
1139     *
1140     * @return A server transaction.
1141     */
1142    public ServerRequestInterface newSIPServerRequest(SIPRequest requestReceived,
1143            MessageChannel requestMessageChannel) {
1144        // Iterator through all server transactions
1145        Iterator<SIPServerTransaction> transactionIterator;
1146        // Next transaction in the set
1147        SIPServerTransaction nextTransaction;
1148        // Transaction to handle this request
1149        SIPServerTransaction currentTransaction;
1150
1151        String key = requestReceived.getTransactionId();
1152
1153        requestReceived.setMessageChannel(requestMessageChannel);
1154
1155        currentTransaction = (SIPServerTransaction) serverTransactionTable.get(key);
1156
1157        // Got to do this for bacasswards compatibility.
1158        if (currentTransaction == null
1159                || !currentTransaction.isMessagePartOfTransaction(requestReceived)) {
1160
1161            // Loop through all server transactions
1162            transactionIterator = serverTransactionTable.values().iterator();
1163            currentTransaction = null;
1164            if (!key.toLowerCase().startsWith(SIPConstants.BRANCH_MAGIC_COOKIE_LOWER_CASE)) {
1165                while (transactionIterator.hasNext() && currentTransaction == null) {
1166
1167                    nextTransaction = (SIPServerTransaction) transactionIterator.next();
1168
1169                    // If this transaction should handle this request,
1170                    if (nextTransaction.isMessagePartOfTransaction(requestReceived)) {
1171                        // Mark this transaction as the one
1172                        // to handle this message
1173                        currentTransaction = nextTransaction;
1174                    }
1175                }
1176            }
1177
1178            // If no transaction exists to handle this message
1179            if (currentTransaction == null) {
1180                currentTransaction = findPendingTransaction(requestReceived);
1181                if (currentTransaction != null) {
1182                    // Associate the tx with the received request.
1183                    requestReceived.setTransaction(currentTransaction);
1184                    if (currentTransaction != null && currentTransaction.acquireSem())
1185                        return currentTransaction;
1186                    else
1187                        return null;
1188
1189                }
1190                // Creating a new server tx. May fail under heavy load.
1191                currentTransaction = createServerTransaction(requestMessageChannel);
1192                if (currentTransaction != null) {
1193                    // currentTransaction.setPassToListener();
1194                    currentTransaction.setOriginalRequest(requestReceived);
1195                    // Associate the tx with the received request.
1196                    requestReceived.setTransaction(currentTransaction);
1197                }
1198
1199            }
1200
1201        }
1202
1203        // Set ths transaction's encapsulated request
1204        // interface from the superclass
1205        if (stackLogger.isLoggingEnabled()) {
1206            stackLogger.logDebug("newSIPServerRequest( " + requestReceived.getMethod() + ":"
1207                    + requestReceived.getTopmostVia().getBranch() + "):" + currentTransaction);
1208        }
1209
1210        if (currentTransaction != null)
1211            currentTransaction.setRequestInterface(sipMessageFactory.newSIPServerRequest(
1212                    requestReceived, currentTransaction));
1213
1214        if (currentTransaction != null && currentTransaction.acquireSem()) {
1215            return currentTransaction;
1216        } else if (currentTransaction != null) {
1217            try {
1218                /*
1219                 * Already processing a message for this transaction.
1220                 * SEND a trying ( message already being processed ).
1221                 */
1222                if (currentTransaction.isMessagePartOfTransaction(requestReceived) &&
1223                    currentTransaction.getMethod().equals(requestReceived.getMethod())) {
1224                    SIPResponse trying = requestReceived.createResponse(Response.TRYING);
1225                    trying.removeContent();
1226                    currentTransaction.getMessageChannel().sendMessage(trying);
1227                }
1228            } catch (Exception ex) {
1229            	if (isLoggingEnabled())
1230            		stackLogger.logError("Exception occured sending TRYING");
1231            }
1232            return null;
1233        } else {
1234            return null;
1235        }
1236    }
1237
1238    /**
1239     * Handles a new SIP response. It finds a client transaction to handle this message. If none
1240     * exists, it sends the message directly to the superclass.
1241     *
1242     * @param responseReceived Response to handle.
1243     * @param responseMessageChannel Channel that received message.
1244     *
1245     * @return A client transaction.
1246     */
1247    public ServerResponseInterface newSIPServerResponse(SIPResponse responseReceived,
1248            MessageChannel responseMessageChannel) {
1249
1250        // Iterator through all client transactions
1251        Iterator<SIPClientTransaction> transactionIterator;
1252        // Next transaction in the set
1253        SIPClientTransaction nextTransaction;
1254        // Transaction to handle this request
1255        SIPClientTransaction currentTransaction;
1256
1257        String key = responseReceived.getTransactionId();
1258
1259        // Note that for RFC 3261 compliant operation, this lookup will
1260        // return a tx if one exists and hence no need to search through
1261        // the table.
1262        currentTransaction = (SIPClientTransaction) clientTransactionTable.get(key);
1263
1264        if (currentTransaction == null
1265                || (!currentTransaction.isMessagePartOfTransaction(responseReceived) && !key
1266                        .startsWith(SIPConstants.BRANCH_MAGIC_COOKIE_LOWER_CASE))) {
1267            // Loop through all client transactions
1268
1269            transactionIterator = clientTransactionTable.values().iterator();
1270            currentTransaction = null;
1271            while (transactionIterator.hasNext() && currentTransaction == null) {
1272
1273                nextTransaction = (SIPClientTransaction) transactionIterator.next();
1274
1275                // If this transaction should handle this request,
1276                if (nextTransaction.isMessagePartOfTransaction(responseReceived)) {
1277
1278                    // Mark this transaction as the one to
1279                    // handle this message
1280                    currentTransaction = nextTransaction;
1281
1282                }
1283
1284            }
1285
1286            // If no transaction exists to handle this message,
1287            if (currentTransaction == null) {
1288                // JvB: Need to log before passing the response to the client
1289                // app, it
1290                // gets modified!
1291                if (this.stackLogger.isLoggingEnabled(StackLogger.TRACE_INFO)) {
1292                    responseMessageChannel.logResponse(responseReceived, System
1293                            .currentTimeMillis(), "before processing");
1294                }
1295
1296                // Pass the message directly to the TU
1297                return sipMessageFactory.newSIPServerResponse(responseReceived,
1298                        responseMessageChannel);
1299
1300            }
1301        }
1302
1303        // Aquire the sem -- previous request may still be processing.
1304        boolean acquired = currentTransaction.acquireSem();
1305        // Set ths transaction's encapsulated response interface
1306        // from the superclass
1307        if (this.stackLogger.isLoggingEnabled(StackLogger.TRACE_INFO)) {
1308            currentTransaction.logResponse(responseReceived, System.currentTimeMillis(),
1309                    "before processing");
1310        }
1311
1312        if (acquired) {
1313            ServerResponseInterface sri = sipMessageFactory.newSIPServerResponse(
1314                    responseReceived, currentTransaction);
1315            if (sri != null) {
1316                currentTransaction.setResponseInterface(sri);
1317            } else {
1318                if (this.stackLogger.isLoggingEnabled()) {
1319                    this.stackLogger.logDebug("returning null - serverResponseInterface is null!");
1320                }
1321                currentTransaction.releaseSem();
1322                return null;
1323            }
1324        } else {
1325        	if (stackLogger.isLoggingEnabled())
1326        		this.stackLogger.logDebug("Could not aquire semaphore !!");
1327        }
1328
1329        if (acquired)
1330            return currentTransaction;
1331        else
1332            return null;
1333
1334    }
1335
1336    /**
1337     * Creates a client transaction to handle a new request. Gets the real message channel from
1338     * the superclass, and then creates a new client transaction wrapped around this channel.
1339     *
1340     * @param nextHop Hop to create a channel to contact.
1341     */
1342    public MessageChannel createMessageChannel(SIPRequest request, MessageProcessor mp,
1343            Hop nextHop) throws IOException {
1344        // New client transaction to return
1345        SIPTransaction returnChannel;
1346
1347        // Create a new client transaction around the
1348        // superclass' message channel
1349        // Create the host/port of the target hop
1350        Host targetHost = new Host();
1351        targetHost.setHostname(nextHop.getHost());
1352        HostPort targetHostPort = new HostPort();
1353        targetHostPort.setHost(targetHost);
1354        targetHostPort.setPort(nextHop.getPort());
1355        MessageChannel mc = mp.createMessageChannel(targetHostPort);
1356
1357        // Superclass will return null if no message processor
1358        // available for the transport.
1359        if (mc == null)
1360            return null;
1361
1362        returnChannel = createClientTransaction(request, mc);
1363
1364        ((SIPClientTransaction) returnChannel).setViaPort(nextHop.getPort());
1365        ((SIPClientTransaction) returnChannel).setViaHost(nextHop.getHost());
1366        addTransactionHash(returnChannel);
1367        // clientTransactionTable.put(returnChannel.getTransactionId(),
1368        // returnChannel);
1369        // Add the transaction timer for the state machine.
1370        // returnChannel.startTransactionTimer();
1371        return returnChannel;
1372
1373    }
1374
1375    /**
1376     * Creates a client transaction that encapsulates a MessageChannel. Useful for implementations
1377     * that want to subclass the standard
1378     *
1379     * @param encapsulatedMessageChannel Message channel of the transport layer.
1380     */
1381    public SIPClientTransaction createClientTransaction(SIPRequest sipRequest,
1382            MessageChannel encapsulatedMessageChannel) {
1383        SIPClientTransaction ct = new SIPClientTransaction(this, encapsulatedMessageChannel);
1384        ct.setOriginalRequest(sipRequest);
1385        return ct;
1386    }
1387
1388    /**
1389     * Creates a server transaction that encapsulates a MessageChannel. Useful for implementations
1390     * that want to subclass the standard
1391     *
1392     * @param encapsulatedMessageChannel Message channel of the transport layer.
1393     */
1394    public SIPServerTransaction createServerTransaction(MessageChannel encapsulatedMessageChannel) {
1395    	// Issue 256 : be consistent with createClientTransaction, if unlimitedServerTransactionTableSize is true,
1396    	// a new Server Transaction is created no matter what
1397        if (unlimitedServerTransactionTableSize) {
1398            return new SIPServerTransaction(this, encapsulatedMessageChannel);
1399        } else {
1400            float threshold = ((float) (serverTransactionTable.size() - serverTransactionTableLowaterMark))
1401                    / ((float) (serverTransactionTableHighwaterMark - serverTransactionTableLowaterMark));
1402            boolean decision = Math.random() > 1.0 - threshold;
1403            if (decision) {
1404                return null;
1405            } else {
1406                return new SIPServerTransaction(this, encapsulatedMessageChannel);
1407            }
1408
1409        }
1410
1411    }
1412
1413    /**
1414     * Get the size of the client transaction table.
1415     *
1416     * @return -- size of the ct table.
1417     */
1418    public int getClientTransactionTableSize() {
1419        return this.clientTransactionTable.size();
1420    }
1421
1422    /**
1423     * Get the size of the server transaction table.
1424     *
1425     * @return -- size of the server table.
1426     */
1427    public int getServerTransactionTableSize() {
1428        return this.serverTransactionTable.size();
1429    }
1430
1431    /**
1432     * Add a new client transaction to the set of existing transactions. Add it to the top of the
1433     * list so an incoming response has less work to do in order to find the transaction.
1434     *
1435     * @param clientTransaction -- client transaction to add to the set.
1436     */
1437    public void addTransaction(SIPClientTransaction clientTransaction) {
1438        if (stackLogger.isLoggingEnabled())
1439            stackLogger.logDebug("added transaction " + clientTransaction);
1440        addTransactionHash(clientTransaction);
1441
1442    }
1443
1444    /**
1445     * Remove transaction. This actually gets the tx out of the search structures which the stack
1446     * keeps around. When the tx
1447     */
1448    public void removeTransaction(SIPTransaction sipTransaction) {
1449        if (stackLogger.isLoggingEnabled()) {
1450            stackLogger.logDebug("Removing Transaction = " + sipTransaction.getTransactionId()
1451                    + " transaction = " + sipTransaction);
1452        }
1453        if (sipTransaction instanceof SIPServerTransaction) {
1454            if (stackLogger.isLoggingEnabled())
1455                stackLogger.logStackTrace();
1456            String key = sipTransaction.getTransactionId();
1457            Object removed = serverTransactionTable.remove(key);
1458            String method = sipTransaction.getMethod();
1459            this.removePendingTransaction((SIPServerTransaction) sipTransaction);
1460            this.removeTransactionPendingAck((SIPServerTransaction) sipTransaction);
1461            if (method.equalsIgnoreCase(Request.INVITE)) {
1462                this.removeFromMergeTable((SIPServerTransaction) sipTransaction);
1463            }
1464            // Send a notification to the listener.
1465            SipProviderImpl sipProvider = (SipProviderImpl) sipTransaction.getSipProvider();
1466            if (removed != null && sipTransaction.testAndSetTransactionTerminatedEvent()) {
1467                TransactionTerminatedEvent event = new TransactionTerminatedEvent(sipProvider,
1468                        (ServerTransaction) sipTransaction);
1469
1470                sipProvider.handleEvent(event, sipTransaction);
1471
1472            }
1473        } else {
1474
1475            String key = sipTransaction.getTransactionId();
1476            Object removed = clientTransactionTable.remove(key);
1477
1478            if (stackLogger.isLoggingEnabled()) {
1479                stackLogger.logDebug("REMOVED client tx " + removed + " KEY = " + key);
1480                if ( removed != null ) {
1481                   SIPClientTransaction clientTx = (SIPClientTransaction)removed;
1482                   if ( clientTx.getMethod().equals(Request.INVITE) && this.maxForkTime != 0 ) {
1483                       RemoveForkedTransactionTimerTask ttask = new RemoveForkedTransactionTimerTask(clientTx);
1484                       this.timer.schedule(ttask, this.maxForkTime * 1000);
1485                   }
1486                }
1487            }
1488
1489            // Send a notification to the listener.
1490            if (removed != null && sipTransaction.testAndSetTransactionTerminatedEvent()) {
1491                SipProviderImpl sipProvider = (SipProviderImpl) sipTransaction.getSipProvider();
1492                TransactionTerminatedEvent event = new TransactionTerminatedEvent(sipProvider,
1493                        (ClientTransaction) sipTransaction);
1494
1495                sipProvider.handleEvent(event, sipTransaction);
1496            }
1497
1498        }
1499    }
1500
1501    /**
1502     * Add a new server transaction to the set of existing transactions. Add it to the top of the
1503     * list so an incoming ack has less work to do in order to find the transaction.
1504     *
1505     * @param serverTransaction -- server transaction to add to the set.
1506     */
1507    public void addTransaction(SIPServerTransaction serverTransaction) throws IOException {
1508        if (stackLogger.isLoggingEnabled())
1509            stackLogger.logDebug("added transaction " + serverTransaction);
1510        serverTransaction.map();
1511
1512        addTransactionHash(serverTransaction);
1513
1514    }
1515
1516    /**
1517     * Hash table for quick lookup of transactions. Here we wait for room if needed.
1518     */
1519    private void addTransactionHash(SIPTransaction sipTransaction) {
1520        SIPRequest sipRequest = sipTransaction.getOriginalRequest();
1521        if (sipTransaction instanceof SIPClientTransaction) {
1522            if (!this.unlimitedClientTransactionTableSize) {
1523                if (this.activeClientTransactionCount.get() > clientTransactionTableHiwaterMark) {
1524                    try {
1525                        synchronized (this.clientTransactionTable) {
1526                            this.clientTransactionTable.wait();
1527                            this.activeClientTransactionCount.incrementAndGet();
1528                        }
1529
1530                    } catch (Exception ex) {
1531                        if (stackLogger.isLoggingEnabled()) {
1532                            stackLogger.logError("Exception occured while waiting for room", ex);
1533                        }
1534
1535                    }
1536                }
1537            } else {
1538                this.activeClientTransactionCount.incrementAndGet();
1539            }
1540            String key = sipRequest.getTransactionId();
1541            clientTransactionTable.put(key, (SIPClientTransaction) sipTransaction);
1542
1543            if (stackLogger.isLoggingEnabled()) {
1544                stackLogger.logDebug(" putTransactionHash : " + " key = " + key);
1545            }
1546        } else {
1547            String key = sipRequest.getTransactionId();
1548
1549            if (stackLogger.isLoggingEnabled()) {
1550                stackLogger.logDebug(" putTransactionHash : " + " key = " + key);
1551            }
1552            serverTransactionTable.put(key, (SIPServerTransaction) sipTransaction);
1553
1554        }
1555
1556    }
1557
1558    /**
1559     * This method is called when a client tx transitions to the Completed or Terminated state.
1560     *
1561     */
1562    protected void decrementActiveClientTransactionCount() {
1563
1564        if (this.activeClientTransactionCount.decrementAndGet() <= this.clientTransactionTableLowaterMark
1565                && !this.unlimitedClientTransactionTableSize) {
1566            synchronized (this.clientTransactionTable) {
1567
1568                clientTransactionTable.notify();
1569
1570            }
1571        }
1572    }
1573
1574    /**
1575     * Remove the transaction from transaction hash.
1576     */
1577    protected void removeTransactionHash(SIPTransaction sipTransaction) {
1578        SIPRequest sipRequest = sipTransaction.getOriginalRequest();
1579        if (sipRequest == null)
1580            return;
1581        if (sipTransaction instanceof SIPClientTransaction) {
1582            String key = sipTransaction.getTransactionId();
1583            if (stackLogger.isLoggingEnabled()) {
1584                stackLogger.logStackTrace();
1585                stackLogger.logDebug("removing client Tx : " + key);
1586            }
1587            clientTransactionTable.remove(key);
1588
1589        } else if (sipTransaction instanceof SIPServerTransaction) {
1590            String key = sipTransaction.getTransactionId();
1591            serverTransactionTable.remove(key);
1592            if (stackLogger.isLoggingEnabled()) {
1593                stackLogger.logDebug("removing server Tx : " + key);
1594            }
1595        }
1596    }
1597
1598    /**
1599     * Invoked when an error has ocurred with a transaction.
1600     *
1601     * @param transactionErrorEvent Error event.
1602     */
1603    public synchronized void transactionErrorEvent(SIPTransactionErrorEvent transactionErrorEvent) {
1604        SIPTransaction transaction = (SIPTransaction) transactionErrorEvent.getSource();
1605
1606        if (transactionErrorEvent.getErrorID() == SIPTransactionErrorEvent.TRANSPORT_ERROR) {
1607            // Kill scanning of this transaction.
1608            transaction.setState(SIPTransaction.TERMINATED_STATE);
1609            if (transaction instanceof SIPServerTransaction) {
1610                // let the reaper get him
1611                ((SIPServerTransaction) transaction).collectionTime = 0;
1612            }
1613            transaction.disableTimeoutTimer();
1614            transaction.disableRetransmissionTimer();
1615            // Send a IO Exception to the Listener.
1616        }
1617    }
1618
1619    /*
1620     * (non-Javadoc)
1621     * @see gov.nist.javax.sip.stack.SIPDialogEventListener#dialogErrorEvent(gov.nist.javax.sip.stack.SIPDialogErrorEvent)
1622     */
1623    public synchronized void dialogErrorEvent(SIPDialogErrorEvent dialogErrorEvent) {
1624        SIPDialog sipDialog = (SIPDialog) dialogErrorEvent.getSource();
1625        SipListener sipListener = ((SipStackImpl)this).getSipListener();
1626        // if the app is not implementing the SipListenerExt interface we delete the dialog to avoid leaks
1627        if(sipDialog != null && !(sipListener instanceof SipListenerExt)) {
1628        	sipDialog.delete();
1629        }
1630    }
1631
1632    /**
1633     * Stop stack. Clear all the timer stuff. Make the stack close all accept connections and
1634     * return. This is useful if you want to start/stop the stack several times from your
1635     * application. Caution : use of this function could cause peculiar bugs as messages are
1636     * prcessed asynchronously by the stack.
1637     */
1638    public void stopStack() {
1639        // Prevent NPE on two concurrent stops
1640        if (this.timer != null)
1641            this.timer.cancel();
1642
1643        // JvB: set it to null, SIPDialog tries to schedule things after stop
1644        timer = null;
1645        this.pendingTransactions.clear();
1646        this.toExit = true;
1647        synchronized (this) {
1648            this.notifyAll();
1649        }
1650        synchronized (this.clientTransactionTable) {
1651            clientTransactionTable.notifyAll();
1652        }
1653
1654        synchronized (this.messageProcessors) {
1655            // Threads must periodically check this flag.
1656            MessageProcessor[] processorList;
1657            processorList = getMessageProcessors();
1658            for (int processorIndex = 0; processorIndex < processorList.length; processorIndex++) {
1659                removeMessageProcessor(processorList[processorIndex]);
1660            }
1661            this.ioHandler.closeAll();
1662            // Let the processing complete.
1663
1664        }
1665        try {
1666
1667            Thread.sleep(1000);
1668
1669        } catch (InterruptedException ex) {
1670        }
1671        this.clientTransactionTable.clear();
1672        this.serverTransactionTable.clear();
1673
1674        this.dialogTable.clear();
1675        this.serverLogger.closeLogFile();
1676
1677    }
1678
1679    /**
1680     * Put a transaction in the pending transaction list. This is to avoid a race condition when a
1681     * duplicate may arrive when the application is deciding whether to create a transaction or
1682     * not.
1683     */
1684    public void putPendingTransaction(SIPServerTransaction tr) {
1685        if (stackLogger.isLoggingEnabled())
1686            stackLogger.logDebug("putPendingTransaction: " + tr);
1687
1688        this.pendingTransactions.put(tr.getTransactionId(), tr);
1689
1690    }
1691
1692    /**
1693     * Return the network layer (i.e. the interface for socket creation or the socket factory for
1694     * the stack).
1695     *
1696     * @return -- the registered Network Layer.
1697     */
1698    public NetworkLayer getNetworkLayer() {
1699        if (networkLayer == null) {
1700            return DefaultNetworkLayer.SINGLETON;
1701        } else {
1702            return networkLayer;
1703        }
1704    }
1705
1706    /**
1707     * Return true if logging is enabled for this stack.
1708     *
1709     * @return true if logging is enabled for this stack instance.
1710     */
1711    public boolean isLoggingEnabled() {
1712        return this.stackLogger == null ? false : this.stackLogger.isLoggingEnabled();
1713    }
1714
1715    /**
1716     * Get the logger.
1717     *
1718     * @return --the logger for the sip stack. Each stack has its own logger instance.
1719     */
1720    public StackLogger getStackLogger() {
1721        return this.stackLogger;
1722    }
1723
1724    /**
1725     * Server log is the place where we log messages for the signaling trace viewer.
1726     *
1727     * @return -- the log file where messages are logged for viewing by the trace viewer.
1728     */
1729    public ServerLogger getServerLogger() {
1730        return this.serverLogger;
1731    }
1732
1733    /**
1734     * Maximum size of a single TCP message. Limiting the size of a single TCP message prevents
1735     * flooding attacks.
1736     *
1737     * @return the size of a single TCP message.
1738     */
1739    public int getMaxMessageSize() {
1740        return this.maxMessageSize;
1741    }
1742
1743    /**
1744     * Set the flag that instructs the stack to only start a single thread for sequentially
1745     * processing incoming udp messages (thus serializing the processing). Same as setting thread
1746     * pool size to 1.
1747     */
1748    public void setSingleThreaded() {
1749        this.threadPoolSize = 1;
1750    }
1751
1752    /**
1753     * Set the thread pool size for processing incoming UDP messages. Limit the total number of
1754     * threads for processing udp messages.
1755     *
1756     * @param size -- the thread pool size.
1757     *
1758     */
1759    public void setThreadPoolSize(int size) {
1760        this.threadPoolSize = size;
1761    }
1762
1763    /**
1764     * Set the max # of simultaneously handled TCP connections.
1765     *
1766     * @param nconnections -- the number of connections to handle.
1767     */
1768    public void setMaxConnections(int nconnections) {
1769        this.maxConnections = nconnections;
1770    }
1771
1772    /**
1773     * Get the default route string.
1774     *
1775     * @param sipRequest is the request for which we want to compute the next hop.
1776     * @throws SipException
1777     */
1778    public Hop getNextHop(SIPRequest sipRequest) throws SipException {
1779        if (this.useRouterForAll) {
1780            // Use custom router to route all messages.
1781            if (router != null)
1782                return router.getNextHop(sipRequest);
1783            else
1784                return null;
1785        } else {
1786            // Also non-SIP request containing Route headers goes to the default
1787            // router
1788            if (sipRequest.getRequestURI().isSipURI() || sipRequest.getRouteHeaders() != null) {
1789                return defaultRouter.getNextHop(sipRequest);
1790            } else if (router != null) {
1791                return router.getNextHop(sipRequest);
1792            } else
1793                return null;
1794        }
1795    }
1796
1797    /**
1798     * Set the descriptive name of the stack.
1799     *
1800     * @param stackName -- descriptive name of the stack.
1801     */
1802    public void setStackName(String stackName) {
1803        this.stackName = stackName;
1804    }
1805
1806
1807
1808    /**
1809     * Set my address.
1810     *
1811     * @param stackAddress -- A string containing the stack address.
1812     */
1813    protected void setHostAddress(String stackAddress) throws UnknownHostException {
1814        if (stackAddress.indexOf(':') != stackAddress.lastIndexOf(':')
1815                && stackAddress.trim().charAt(0) != '[')
1816            this.stackAddress = '[' + stackAddress + ']';
1817        else
1818            this.stackAddress = stackAddress;
1819        this.stackInetAddress = InetAddress.getByName(stackAddress);
1820    }
1821
1822    /**
1823     * Get my address.
1824     *
1825     * @return hostAddress - my host address or null if no host address is defined.
1826     * @deprecated
1827     */
1828    public String getHostAddress() {
1829
1830        // JvB: for 1.2 this may return null...
1831        return this.stackAddress;
1832    }
1833
1834    /**
1835     * Set the router algorithm. This is meant for routing messages out of dialog or for non-sip
1836     * uri's.
1837     *
1838     * @param router A class that implements the Router interface.
1839     */
1840    protected void setRouter(Router router) {
1841        this.router = router;
1842    }
1843
1844    /**
1845     * Get the router algorithm.
1846     *
1847     * @return Router router
1848     */
1849    public Router getRouter(SIPRequest request) {
1850        if (request.getRequestLine() == null) {
1851            return this.defaultRouter;
1852        } else if (this.useRouterForAll) {
1853            return this.router;
1854        } else {
1855            if (request.getRequestURI().getScheme().equals("sip")
1856                    || request.getRequestURI().getScheme().equals("sips")) {
1857                return this.defaultRouter;
1858            } else {
1859                if (this.router != null)
1860                    return this.router;
1861                else
1862                    return defaultRouter;
1863            }
1864        }
1865    }
1866
1867    /*
1868     * (non-Javadoc)
1869     *
1870     * @see javax.sip.SipStack#getRouter()
1871     */
1872    public Router getRouter() {
1873        return this.router;
1874    }
1875
1876    /**
1877     * return the status of the toExit flag.
1878     *
1879     * @return true if the stack object is alive and false otherwise.
1880     */
1881    public boolean isAlive() {
1882        return !toExit;
1883    }
1884
1885    /**
1886     * Adds a new MessageProcessor to the list of running processors for this SIPStack and starts
1887     * it. You can use this method for dynamic stack configuration.
1888     */
1889    protected void addMessageProcessor(MessageProcessor newMessageProcessor) throws IOException {
1890        synchronized (messageProcessors) {
1891            // Suggested changes by Jeyashankher, jai@lucent.com
1892            // newMessageProcessor.start() can fail
1893            // because a local port is not available
1894            // This throws an IOException.
1895            // We should not add the message processor to the
1896            // local list of processors unless the start()
1897            // call is successful.
1898            // newMessageProcessor.start();
1899            messageProcessors.add(newMessageProcessor);
1900
1901        }
1902    }
1903
1904    /**
1905     * Removes a MessageProcessor from this SIPStack.
1906     *
1907     * @param oldMessageProcessor
1908     */
1909    protected void removeMessageProcessor(MessageProcessor oldMessageProcessor) {
1910        synchronized (messageProcessors) {
1911            if (messageProcessors.remove(oldMessageProcessor)) {
1912                oldMessageProcessor.stop();
1913            }
1914        }
1915    }
1916
1917    /**
1918     * Gets an array of running MessageProcessors on this SIPStack. Acknowledgement: Jeff Keyser
1919     * suggested that applications should have access to the running message processors and
1920     * contributed this code.
1921     *
1922     * @return an array of running message processors.
1923     */
1924    protected MessageProcessor[] getMessageProcessors() {
1925        synchronized (messageProcessors) {
1926            return (MessageProcessor[]) messageProcessors.toArray(new MessageProcessor[0]);
1927        }
1928    }
1929
1930    /**
1931     * Creates the equivalent of a JAIN listening point and attaches to the stack.
1932     *
1933     * @param ipAddress -- ip address for the listening point.
1934     * @param port -- port for the listening point.
1935     * @param transport -- transport for the listening point.
1936     */
1937    protected MessageProcessor createMessageProcessor(InetAddress ipAddress, int port,
1938            String transport) throws java.io.IOException {
1939        if (transport.equalsIgnoreCase("udp")) {
1940            UDPMessageProcessor udpMessageProcessor = new UDPMessageProcessor(ipAddress, this,
1941                    port);
1942            this.addMessageProcessor(udpMessageProcessor);
1943            this.udpFlag = true;
1944            return udpMessageProcessor;
1945        } else if (transport.equalsIgnoreCase("tcp")) {
1946            TCPMessageProcessor tcpMessageProcessor = new TCPMessageProcessor(ipAddress, this,
1947                    port);
1948            this.addMessageProcessor(tcpMessageProcessor);
1949            // this.tcpFlag = true;
1950            return tcpMessageProcessor;
1951        } else if (transport.equalsIgnoreCase("tls")) {
1952            TLSMessageProcessor tlsMessageProcessor = new TLSMessageProcessor(ipAddress, this,
1953                    port);
1954            this.addMessageProcessor(tlsMessageProcessor);
1955            // this.tlsFlag = true;
1956            return tlsMessageProcessor;
1957        } else if (transport.equalsIgnoreCase("sctp")) {
1958
1959        	// Need Java 7 for this, so these classes are packaged in a separate jar
1960        	// Try to load it indirectly, if fails report an error
1961        	try {
1962				Class<?> mpc = ClassLoader.getSystemClassLoader().loadClass( "gov.nist.javax.sip.stack.sctp.SCTPMessageProcessor" );
1963				MessageProcessor mp = (MessageProcessor) mpc.newInstance();
1964				mp.initialize( ipAddress, port, this );
1965				this.addMessageProcessor(mp);
1966				return mp;
1967			} catch (ClassNotFoundException e) {
1968				throw new IllegalArgumentException("SCTP not supported (needs Java 7 and SCTP jar in classpath)");
1969			} catch ( InstantiationException ie ) {
1970				throw new IllegalArgumentException("Error initializing SCTP", ie);
1971			} catch ( IllegalAccessException ie ) {
1972				throw new IllegalArgumentException("Error initializing SCTP", ie);
1973			}
1974        } else {
1975            throw new IllegalArgumentException("bad transport");
1976        }
1977
1978    }
1979
1980    /**
1981     * Set the message factory.
1982     *
1983     * @param messageFactory -- messageFactory to set.
1984     */
1985    protected void setMessageFactory(StackMessageFactory messageFactory) {
1986        this.sipMessageFactory = messageFactory;
1987    }
1988
1989    /**
1990     * Creates a new MessageChannel for a given Hop.
1991     *
1992     * @param sourceIpAddress - Ip address of the source of this message.
1993     *
1994     * @param sourcePort - source port of the message channel to be created.
1995     *
1996     * @param nextHop Hop to create a MessageChannel to.
1997     *
1998     * @return A MessageChannel to the specified Hop, or null if no MessageProcessors support
1999     *         contacting that Hop.
2000     *
2001     * @throws UnknownHostException If the host in the Hop doesn't exist.
2002     */
2003    public MessageChannel createRawMessageChannel(String sourceIpAddress, int sourcePort,
2004            Hop nextHop) throws UnknownHostException {
2005        Host targetHost;
2006        HostPort targetHostPort;
2007        Iterator processorIterator;
2008        MessageProcessor nextProcessor;
2009        MessageChannel newChannel;
2010
2011        // Create the host/port of the target hop
2012        targetHost = new Host();
2013        targetHost.setHostname(nextHop.getHost());
2014        targetHostPort = new HostPort();
2015        targetHostPort.setHost(targetHost);
2016        targetHostPort.setPort(nextHop.getPort());
2017
2018        // Search each processor for the correct transport
2019        newChannel = null;
2020        processorIterator = messageProcessors.iterator();
2021        while (processorIterator.hasNext() && newChannel == null) {
2022            nextProcessor = (MessageProcessor) processorIterator.next();
2023            // If a processor that supports the correct
2024            // transport is found,
2025            if (nextHop.getTransport().equalsIgnoreCase(nextProcessor.getTransport())
2026                    && sourceIpAddress.equals(nextProcessor.getIpAddress().getHostAddress())
2027                    && sourcePort == nextProcessor.getPort()) {
2028                try {
2029                    // Create a channel to the target
2030                    // host/port
2031                    newChannel = nextProcessor.createMessageChannel(targetHostPort);
2032                } catch (UnknownHostException ex) {
2033                    if (stackLogger.isLoggingEnabled())
2034                        stackLogger.logException(ex);
2035                    throw ex;
2036                } catch (IOException e) {
2037                    if (stackLogger.isLoggingEnabled())
2038                        stackLogger.logException(e);
2039                    // Ignore channel creation error -
2040                    // try next processor
2041                }
2042            }
2043        }
2044        // Return the newly-created channel
2045        return newChannel;
2046    }
2047
2048    /**
2049     * Return true if a given event can result in a forked subscription. The stack is configured
2050     * with a set of event names that can result in forked subscriptions.
2051     *
2052     * @param ename -- event name to check.
2053     *
2054     */
2055    public boolean isEventForked(String ename) {
2056        if (stackLogger.isLoggingEnabled()) {
2057            stackLogger.logDebug("isEventForked: " + ename + " returning "
2058                    + this.forkedEvents.contains(ename));
2059        }
2060        return this.forkedEvents.contains(ename);
2061    }
2062
2063    /**
2064     * get the address resolver interface.
2065     *
2066     * @return -- the registered address resolver.
2067     */
2068    public AddressResolver getAddressResolver() {
2069        return this.addressResolver;
2070    }
2071
2072    /**
2073     * Set the address resolution interface
2074     *
2075     * @param addressResolver -- the address resolver to set.
2076     */
2077    public void setAddressResolver(AddressResolver addressResolver) {
2078        this.addressResolver = addressResolver;
2079    }
2080
2081    /**
2082     * Set the logger factory.
2083     *
2084     * @param logRecordFactory -- the log record factory to set.
2085     */
2086    public void setLogRecordFactory(LogRecordFactory logRecordFactory) {
2087        this.logRecordFactory = logRecordFactory;
2088    }
2089
2090    /**
2091     * get the thread auditor object
2092     *
2093     * @return -- the thread auditor of the stack
2094     */
2095    public ThreadAuditor getThreadAuditor() {
2096        return this.threadAuditor;
2097    }
2098
2099    // /
2100    // / Stack Audit methods
2101    // /
2102
2103    /**
2104     * Audits the SIP Stack for leaks
2105     *
2106     * @return Audit report, null if no leaks were found
2107     */
2108    public String auditStack(Set activeCallIDs, long leakedDialogTimer,
2109            long leakedTransactionTimer) {
2110        String auditReport = null;
2111        String leakedDialogs = auditDialogs(activeCallIDs, leakedDialogTimer);
2112        String leakedServerTransactions = auditTransactions(serverTransactionTable,
2113                leakedTransactionTimer);
2114        String leakedClientTransactions = auditTransactions(clientTransactionTable,
2115                leakedTransactionTimer);
2116        if (leakedDialogs != null || leakedServerTransactions != null
2117                || leakedClientTransactions != null) {
2118            auditReport = "SIP Stack Audit:\n" + (leakedDialogs != null ? leakedDialogs : "")
2119                    + (leakedServerTransactions != null ? leakedServerTransactions : "")
2120                    + (leakedClientTransactions != null ? leakedClientTransactions : "");
2121        }
2122        return auditReport;
2123    }
2124
2125    /**
2126     * Audits SIP dialogs for leaks - Compares the dialogs in the dialogTable with a list of Call
2127     * IDs passed by the application. - Dialogs that are not known by the application are leak
2128     * suspects. - Kill the dialogs that are still around after the timer specified.
2129     *
2130     * @return Audit report, null if no dialog leaks were found
2131     */
2132    private String auditDialogs(Set activeCallIDs, long leakedDialogTimer) {
2133        String auditReport = "  Leaked dialogs:\n";
2134        int leakedDialogs = 0;
2135        long currentTime = System.currentTimeMillis();
2136
2137        // Make a shallow copy of the dialog list.
2138        // This copy will remain intact as leaked dialogs are removed by the
2139        // stack.
2140        LinkedList dialogs;
2141        synchronized (dialogTable) {
2142            dialogs = new LinkedList(dialogTable.values());
2143        }
2144
2145        // Iterate through the dialogDialog, get the callID of each dialog and
2146        // check if it's in the
2147        // list of active calls passed by the application. If it isn't, start
2148        // the timer on it.
2149        // If the timer has expired, kill the dialog.
2150        Iterator it = dialogs.iterator();
2151        while (it.hasNext()) {
2152            // Get the next dialog
2153            SIPDialog itDialog = (SIPDialog) it.next();
2154
2155            // Get the call id associated with this dialog
2156            CallIdHeader callIdHeader = (itDialog != null ? itDialog.getCallId() : null);
2157            String callID = (callIdHeader != null ? callIdHeader.getCallId() : null);
2158
2159            // Check if the application knows about this call id
2160            if (itDialog != null && callID != null && !activeCallIDs.contains(callID)) {
2161                // Application doesn't know anything about this dialog...
2162                if (itDialog.auditTag == 0) {
2163                    // Mark this dialog as suspect
2164                    itDialog.auditTag = currentTime;
2165                } else {
2166                    // We already audited this dialog before. Check if his
2167                    // time's up.
2168                    if (currentTime - itDialog.auditTag >= leakedDialogTimer) {
2169                        // Leaked dialog found
2170                        leakedDialogs++;
2171
2172                        // Generate report
2173                        DialogState dialogState = itDialog.getState();
2174                        String dialogReport = "dialog id: " + itDialog.getDialogId()
2175                                + ", dialog state: "
2176                                + (dialogState != null ? dialogState.toString() : "null");
2177                        auditReport += "    " + dialogReport + "\n";
2178
2179                        // Kill it
2180                        itDialog.setState(SIPDialog.TERMINATED_STATE);
2181                        if (stackLogger.isLoggingEnabled())
2182                        	stackLogger.logDebug("auditDialogs: leaked " + dialogReport);
2183                    }
2184                }
2185            }
2186        }
2187
2188        // Return final report
2189        if (leakedDialogs > 0) {
2190            auditReport += "    Total: " + Integer.toString(leakedDialogs)
2191                    + " leaked dialogs detected and removed.\n";
2192        } else {
2193            auditReport = null;
2194        }
2195        return auditReport;
2196    }
2197
2198    /**
2199     * Audits SIP transactions for leaks
2200     *
2201     * @return Audit report, null if no transaction leaks were found
2202     */
2203    private String auditTransactions(ConcurrentHashMap transactionsMap,
2204            long a_nLeakedTransactionTimer) {
2205        String auditReport = "  Leaked transactions:\n";
2206        int leakedTransactions = 0;
2207        long currentTime = System.currentTimeMillis();
2208
2209        // Make a shallow copy of the transaction list.
2210        // This copy will remain intact as leaked transactions are removed by
2211        // the stack.
2212        LinkedList transactionsList = new LinkedList(transactionsMap.values());
2213
2214        // Iterate through our copy
2215        Iterator it = transactionsList.iterator();
2216        while (it.hasNext()) {
2217            SIPTransaction sipTransaction = (SIPTransaction) it.next();
2218            if (sipTransaction != null) {
2219                if (sipTransaction.auditTag == 0) {
2220                    // First time we see this transaction. Mark it as audited.
2221                    sipTransaction.auditTag = currentTime;
2222                } else {
2223                    // We've seen this transaction before. Check if his time's
2224                    // up.
2225                    if (currentTime - sipTransaction.auditTag >= a_nLeakedTransactionTimer) {
2226                        // Leaked transaction found
2227                        leakedTransactions++;
2228
2229                        // Generate some report
2230                        TransactionState transactionState = sipTransaction.getState();
2231                        SIPRequest origRequest = sipTransaction.getOriginalRequest();
2232                        String origRequestMethod = (origRequest != null ? origRequest.getMethod()
2233                                : null);
2234                        String transactionReport = sipTransaction.getClass().getName()
2235                                + ", state: "
2236                                + (transactionState != null ? transactionState.toString()
2237                                        : "null") + ", OR: "
2238                                + (origRequestMethod != null ? origRequestMethod : "null");
2239                        auditReport += "    " + transactionReport + "\n";
2240
2241                        // Kill it
2242                        removeTransaction(sipTransaction);
2243                        if (isLoggingEnabled())
2244                        	stackLogger.logDebug("auditTransactions: leaked " + transactionReport);
2245                    }
2246                }
2247            }
2248        }
2249
2250        // Return final report
2251        if (leakedTransactions > 0) {
2252            auditReport += "    Total: " + Integer.toString(leakedTransactions)
2253                    + " leaked transactions detected and removed.\n";
2254        } else {
2255            auditReport = null;
2256        }
2257        return auditReport;
2258    }
2259
2260    public void setNon2XXAckPassedToListener(boolean passToListener) {
2261        this.non2XXAckPassedToListener = passToListener;
2262    }
2263
2264    /**
2265     * @return the non2XXAckPassedToListener
2266     */
2267    public boolean isNon2XXAckPassedToListener() {
2268        return non2XXAckPassedToListener;
2269    }
2270
2271    /**
2272     * Get the count of client transactions that is not in the completed or terminated state.
2273     *
2274     * @return the activeClientTransactionCount
2275     */
2276    public int getActiveClientTransactionCount() {
2277        return activeClientTransactionCount.get();
2278    }
2279
2280    public boolean isRfc2543Supported() {
2281
2282        return this.rfc2543Supported;
2283    }
2284
2285    public boolean isCancelClientTransactionChecked() {
2286        return this.cancelClientTransactionChecked;
2287    }
2288
2289    public boolean isRemoteTagReassignmentAllowed() {
2290        return this.remoteTagReassignmentAllowed;
2291    }
2292
2293    /**
2294     * This method is slated for addition to the next spec revision.
2295     *
2296     *
2297     * @return -- the collection of dialogs that is being managed by the stack.
2298     */
2299    public Collection<Dialog> getDialogs() {
2300        HashSet<Dialog> dialogs = new HashSet<Dialog>();
2301        dialogs.addAll(this.dialogTable.values());
2302        dialogs.addAll(this.earlyDialogTable.values());
2303        return dialogs;
2304    }
2305
2306    /**
2307     *
2308     * @return -- the collection of dialogs matching the state that is being managed by the stack.
2309     */
2310    public Collection<Dialog> getDialogs(DialogState state) {
2311        HashSet<Dialog> matchingDialogs = new HashSet<Dialog>();
2312        if (DialogState.EARLY.equals(state)) {
2313            matchingDialogs.addAll(this.earlyDialogTable.values());
2314        } else {
2315            Collection<SIPDialog> dialogs = dialogTable.values();
2316            for (SIPDialog dialog : dialogs) {
2317                if (dialog.getState() != null && dialog.getState().equals(state)) {
2318                    matchingDialogs.add(dialog);
2319                }
2320            }
2321        }
2322        return matchingDialogs;
2323    }
2324
2325    /**
2326     * Get the Replaced Dialog from the stack.
2327     *
2328     * @param replacesHeader -- the header that references the dialog being replaced.
2329     */
2330    public Dialog getReplacesDialog(ReplacesHeader replacesHeader) {
2331        String cid = replacesHeader.getCallId();
2332        String fromTag = replacesHeader.getFromTag();
2333        String toTag = replacesHeader.getToTag();
2334
2335        StringBuffer dialogId = new StringBuffer(cid);
2336
2337        // retval.append(COLON).append(to.getUserAtHostPort());
2338        if (toTag != null) {
2339            dialogId.append(":");
2340            dialogId.append(toTag);
2341        }
2342        // retval.append(COLON).append(from.getUserAtHostPort());
2343        if (fromTag != null) {
2344            dialogId.append(":");
2345            dialogId.append(fromTag);
2346        }
2347        String did = dialogId.toString().toLowerCase();
2348        if (stackLogger.isLoggingEnabled())
2349        	stackLogger.logDebug("Looking for dialog " + did);
2350        /*
2351         * Check if we can find this dialog in our dialog table.
2352         */
2353        Dialog replacesDialog =  this.dialogTable.get(did);
2354        /*
2355         * This could be a forked dialog. Search for it.
2356         */
2357        if ( replacesDialog == null ) {
2358           for ( SIPClientTransaction ctx : this.clientTransactionTable.values()) {
2359               if ( ctx.getDialog(did) != null ) {
2360                   replacesDialog = ctx.getDialog(did);
2361                   break;
2362               }
2363           }
2364        }
2365
2366        return replacesDialog;
2367    }
2368
2369    /**
2370     * Get the Join Dialog from the stack.
2371     *
2372     * @param joinHeader -- the header that references the dialog being joined.
2373     */
2374    public Dialog getJoinDialog(JoinHeader joinHeader) {
2375        String cid = joinHeader.getCallId();
2376        String fromTag = joinHeader.getFromTag();
2377        String toTag = joinHeader.getToTag();
2378
2379        StringBuffer retval = new StringBuffer(cid);
2380
2381        // retval.append(COLON).append(to.getUserAtHostPort());
2382        if (toTag != null) {
2383            retval.append(":");
2384            retval.append(toTag);
2385        }
2386        // retval.append(COLON).append(from.getUserAtHostPort());
2387        if (fromTag != null) {
2388            retval.append(":");
2389            retval.append(fromTag);
2390        }
2391        return this.dialogTable.get(retval.toString().toLowerCase());
2392    }
2393
2394    /**
2395     * @param timer the timer to set
2396     */
2397    public void setTimer(Timer timer) {
2398        this.timer = timer;
2399    }
2400
2401    /**
2402     * @return the timer
2403     */
2404    public Timer getTimer() {
2405        return timer;
2406    }
2407
2408
2409    /**
2410     * Size of the receive UDP buffer. This property affects performance under load. Bigger buffer
2411     * is better under load.
2412     *
2413     * @return
2414     */
2415	public int getReceiveUdpBufferSize() {
2416		return receiveUdpBufferSize;
2417	}
2418
2419    /**
2420     * Size of the receive UDP buffer. This property affects performance under load. Bigger buffer
2421     * is better under load.
2422     *
2423     * @return
2424     */
2425	public void setReceiveUdpBufferSize(int receiveUdpBufferSize) {
2426		this.receiveUdpBufferSize = receiveUdpBufferSize;
2427	}
2428
2429    /**
2430     * Size of the send UDP buffer. This property affects performance under load. Bigger buffer
2431     * is better under load.
2432     *
2433     * @return
2434     */
2435	public int getSendUdpBufferSize() {
2436		return sendUdpBufferSize;
2437	}
2438
2439    /**
2440     * Size of the send UDP buffer. This property affects performance under load. Bigger buffer
2441     * is better under load.
2442     *
2443     * @return
2444     */
2445	public void setSendUdpBufferSize(int sendUdpBufferSize) {
2446		this.sendUdpBufferSize = sendUdpBufferSize;
2447	}
2448
2449	/**
2450	 * @param stackLogger the stackLogger to set
2451	 */
2452	public void setStackLogger(StackLogger stackLogger) {
2453		this.stackLogger = stackLogger;
2454	}
2455
2456	 /**
2457	  * Flag that reqests checking of branch IDs on responses.
2458	  *
2459	  * @return
2460	  */
2461	 public boolean checkBranchId() {
2462	       return this.checkBranchId;
2463	 }
2464
2465    /**
2466     * @param logStackTraceOnMessageSend the logStackTraceOnMessageSend to set
2467     */
2468    public void setLogStackTraceOnMessageSend(boolean logStackTraceOnMessageSend) {
2469        this.logStackTraceOnMessageSend = logStackTraceOnMessageSend;
2470    }
2471
2472    /**
2473     * @return the logStackTraceOnMessageSend
2474     */
2475    public boolean isLogStackTraceOnMessageSend() {
2476        return logStackTraceOnMessageSend;
2477    }
2478
2479    public void setDeliverDialogTerminatedEventForNullDialog() {
2480        this.isDialogTerminatedEventDeliveredForNullDialog = true;
2481    }
2482
2483    public void addForkedClientTransaction(SIPClientTransaction clientTransaction) {
2484        this.forkedClientTransactionTable.put(clientTransaction.getTransactionId(), clientTransaction );
2485    }
2486
2487    public SIPClientTransaction getForkedTransaction(String transactionId) {
2488        return this.forkedClientTransactionTable.get(transactionId);
2489    }
2490
2491
2492}
2493