Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Conditions Of Use
    *
    * This software was developed by employees of the National Institute of
    * Standards and Technology (NIST), an agency of the Federal Government.
    * Pursuant to title 15 Untied States Code Section 105, works of NIST
    * employees are not subject to copyright protection in the United States
    * and are considered to be in the public domain.  As a result, a formal
    * license is not needed to use the software.
   *
   * This software is provided by NIST as a service and is expressly
   * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
   * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
   * AND DATA ACCURACY.  NIST does not warrant or make any representations
   * regarding the use of the software or the results thereof, including but
   * not limited to the correctness, accuracy, reliability or usefulness of
   * the software.
   *
   * Permission to use this software is contingent upon your acceptance
   * of the terms of this agreement
   *
   * .
   *
   */
/
  
  /* Product of NIST Advanced Networking Technologies Division      */
/
  
  package gov.nist.javax.sip.stack;
  
  
  import java.util.HashSet;
  import java.util.List;
  import java.util.Random;
  import java.util.Set;
  
 
 /*
  * Acknowledgements:
  * 
  * Bugs in this class were reported by Antonis Karydas, Brad Templeton, Jeff Adams, Alex Rootham ,
  * Martin Le Clerk, Christophe Anzille, Andreas Bystrom, Lebing Xie, Jeroen van Bemmel. Hagai Sela
  * reported a bug in updating the route set (on RE-INVITE). Jens Tinfors submitted a bug fix and
  * the .equals method. Jan Schaumloeffel contributed a buf fix ( memory leak was happening when
  * 180 contained a To tag. Bug fixes by Vladimir Ralev (Redhat).
  * Performance enhancements and memory reduction enhancements by Jean Deruelle.
  * 
  */

Tracks dialogs. A dialog is a peer to peer association of communicating SIP entities. For INVITE transactions, a Dialog is created when a success message is received (i.e. a response that has a To tag). The SIP Protocol stores enough state in the message structure to extract a dialog identifier that can be used to retrieve this structure from the SipStack.

Author(s):
M. Ranganathan
Version:
1.2 $Revision: 1.207 $ $Date: 2010-12-02 22:04:14 $
 
 
 public class SIPDialog implements javax.sip.DialogDialogExt {
 	private static StackLogger logger = CommonLogger.getLogger(SIPDialog.class);
 
     private static final long serialVersionUID = -1429794423085204069L;
 
     private transient boolean dialogTerminatedEventDelivered// prevent
     // duplicate
 
     private transient String stackTrace// for semaphore debugging.
 
     protected String method;
 
     // delivery of the event
     protected transient boolean isAssigned;
 
     protected boolean reInviteFlag;
 
     private transient Object applicationData// Opaque pointer to application
     // data.
 
     private transient SIPRequest originalRequest;
     // jeand : avoid keeping the original request ref above for too long (mem
     // saving)
     protected transient String originalRequestRecordRouteHeadersString;
     protected transient RecordRouteList originalRequestRecordRouteHeaders;
 
     // Last response (JvB: either sent or received).
     // jeand replaced the last response with only the data from it needed to
     // save on mem
     protected String lastResponseDialogId;
     private Via lastResponseTopMostVia;
     protected Integer lastResponseStatusCode;
     protected long lastResponseCSeqNumber;
     protected String lastResponseMethod;
     protected String lastResponseFromTag;
     protected String lastResponseToTag;
 
     // jeand: needed for reliable response sending but nullifyed right after the
     // ACK has been received or sent to let go of the ref ASAP
     protected SIPTransaction firstTransaction;
     // jeand needed for checking 491 but nullifyed right after the ACK has been
     // received or sent to let go of the ref ASAP
     protected SIPTransaction lastTransaction;
 
     protected String dialogId;
 
     protected transient String earlyDialogId;
 
     protected long localSequenceNumber;
 
     protected long remoteSequenceNumber;
 
     protected String myTag;
 
     protected String hisTag;
 
     protected RouteList routeList;
 
     private transient SIPTransactionStack sipStack;
 
     private int dialogState;
 
     protected transient SIPRequest lastAckSent;
 
     // jeand : replaced the lastAckReceived message with only the data needed to
     // save on mem
     protected Long lastAckReceivedCSeqNumber;
 
     // could be set on recovery by examining the method looks like a duplicate
     // of ackSeen
     protected transient boolean ackProcessed;
 
     protected transient DialogTimerTask timerTask;
 
     protected transient long nextSeqno;
 
     private transient int retransmissionTicksLeft;
 
     private transient int prevRetransmissionTicks;
 
     protected long originalLocalSequenceNumber;
 
     // This is for debugging only.
     private transient int ackLine;
 
     // Audit tag used by the SIP Stack audit
     public transient long auditTag = 0;
 
     // The following fields are extracted from the request that created the
     // Dialog.
 
     protected javax.sip.address.Address localParty;
     protected String localPartyStringified;
 
     protected javax.sip.address.Address remoteParty;
     protected String remotePartyStringified;
 
     protected CallIdHeader callIdHeader;
     protected String callIdHeaderString;
 
     public final static int NULL_STATE = -1;
 
     public final static int EARLY_STATE = .;
 
     public final static int CONFIRMED_STATE = .;
 
     public final static int TERMINATED_STATE = .;
 
     // the amount of time to keep this dialog around before the stack GC's it
 
     private static final int DIALOG_LINGER_TIME = 8;
 
     protected boolean serverTransactionFlag;
 
     private transient SipProviderImpl sipProvider;
 
     protected boolean terminateOnBye;
 
     protected transient boolean byeSent// Flag set when BYE is sent, to
     // disallow new
 
     // requests
 
     protected Address remoteTarget;
     protected String remoteTargetStringified;
 
     protected EventHeader eventHeader// for Subscribe notify
 
     // Stores the last OK for the INVITE
     // Used in createAck.
     protected transient long lastInviteOkReceived;
 
     private transient Semaphore ackSem = new Semaphore(1);
 
     protected transient int reInviteWaitTime = 100;
 
     private transient DialogDeleteTask dialogDeleteTask;
 
 
     protected transient boolean isAcknowledged;
 
     private transient long highestSequenceNumberAcknowledged = -1;
 
     protected boolean isBackToBackUserAgent;
 
     protected boolean sequenceNumberValidation = true;
 
     // List of event listeners for this dialog
     private transient Set<SIPDialogEventListenereventListeners;
     // added for Issue 248 :
     // https://jain-sip.dev.java.net/issues/show_bug.cgi?id=248
     private Semaphore timerTaskLock = new Semaphore(1);
 
     // We store here the useful data from the first transaction without having
     // to
     // keep the whole transaction object for the duration of the dialog. It also
     // contains the non-transient information used in the replication of
     // dialogs.
     protected boolean firstTransactionSecure;
     protected boolean firstTransactionSeen;
     protected String firstTransactionMethod;
     protected String firstTransactionId;
     protected boolean firstTransactionIsServerTransaction;
     protected String firstTransactionMergeId;
     protected int firstTransactionPort = 5060;
     protected Contact contactHeader;
     protected String contactHeaderStringified;
 
     private boolean pendingRouteUpdateOn202Response;
 
                                                                  // subequent
                                                                  // requests.
 
     // aggressive flag to optimize eagerly
     private boolean releaseReferences;
 
 
     private int earlyDialogTimeout = 180;
 
 	private int ackSemTakenFor;
 
 	
     // //////////////////////////////////////////////////////
     // Inner classes
     // //////////////////////////////////////////////////////
 
     class EarlyStateTimerTask extends SIPStackTimerTask implements Serializable {
 
         public EarlyStateTimerTask() {
 
         }
 
         @Override
         public void runTask() {
             try {
                 if (SIPDialog.this.getState().equals(.)) {
                     
                     SIPDialog.this
                             .raiseErrorEvent(.);
                 } else {
                     if (.isLoggingEnabled(.)) {
                         .logDebug("EarlyStateTimerTask : Dialog state is " + SIPDialog.this.getState());
                     }
                 }
             } catch (Exception ex) {
                 .logError(
                         "Unexpected exception delivering event"ex);
             }
         }
 
     }

    
This task waits till a pending ACK has been recorded and then sends out a re-INVITE. This is to prevent interleaving INVITEs ( which will result in a 493 from the UA that receives the out of order INVITE). This is primarily for B2BUA support. A B2BUA may send a delayed ACK while it does mid call codec renegotiation. In the meanwhile, it cannot send an intervening re-INVITE otherwise the othr end will respond with a REQUEST_PENDING. We want to avoid this condition. Hence we wait till the ACK for the previous re-INVITE has been sent before sending the next re-INVITE.
 
     public class ReInviteSender implements RunnableSerializable {
         private static final long serialVersionUID = 1019346148741070635L;
         ClientTransaction ctx;
 
         public void terminate() {
             try {
             	if ( .isLoggingEnabled(.)) {
             		.logDebug("ReInviteSender::terminate: ctx = " + );
             	}
             	
                 .terminate();
                 Thread.currentThread().interrupt();
             } catch (ObjectInUseException e) {
                 .logError("unexpected error"e);
             }
         }
 
         public ReInviteSender(ClientTransaction ctx) {
             this. = ctx;
             if ( .isLoggingEnabled(.)) {
             	.logDebug("ReInviteSender::ReInviteSender: ctx = " + ctx );
             	.logStackTrace();
             }
         }
 
         public void run() {
             try {
                 long timeToWait = 0;
                 long startTime = System.currentTimeMillis();
                 boolean dialogTimedOut = false;
                 boolean busyWait = false;
 
                 // If we have an INVITE transaction, make sure that it is TERMINATED
                 // before sending a re-INVITE.. Not the cleanest solution but it works.
                 if (.isLoggingEnabled(.)) {
                 	.logDebug("SIPDialog::reInviteSender: dialog = " + .getDialog()  + " lastTransaction = " +  + " lastTransactionState " + .getState());
                 }
                 if (SIPDialog.this. != null &&
                 			SIPDialog.this. instanceof SIPServerTransaction && 
                 			SIPDialog.this..isInviteTransaction() &&
                 		    SIPDialog.this..getState() != .)
                 {
                 	((SIPServerTransaction)SIPDialog.this.).waitForTermination();
                 	Thread.sleep(50);
                 }
                 
                
 
                 if (!SIPDialog.this.takeAckSem()) {
                     /*
                      * Could not send re-INVITE fire a timeout on the INVITE.
                      */
                     if (.isLoggingEnabled())
                         
                                 .logError(
                                         "Could not send re-INVITE time out ClientTransaction");
                     ((SIPClientTransaction).fireTimeoutTimer();
                     /*
                      * Send BYE to the Dialog.
                      */
                     if (.getSipListener() != null
                             && .getSipListener() instanceof SipListenerExt) {
                         dialogTimedOut = true;
                         raiseErrorEvent(.,(SIPClientTransaction));
                     } else {
                         Request byeRequest = SIPDialog.this
                                 .createRequest(.);
                         if (MessageFactoryImpl.getDefaultUserAgentHeader() != null) {
                             byeRequest.addHeader(MessageFactoryImpl
                                     .getDefaultUserAgentHeader());
                         }
                         ReasonHeader reasonHeader = new Reason();
                         reasonHeader.setCause(1024);
                         reasonHeader.setText("Timed out waiting to re-INVITE");
                         byeRequest.addHeader(reasonHeader);
                         ClientTransaction byeCtx = SIPDialog.this
                                 .getSipProvider().getNewClientTransaction(
                                         byeRequest);
                         SIPDialog.this.sendRequest(byeCtx);
                         return;
                     }
                 }
                 if (getState() != .) {
 
                     timeToWait = System.currentTimeMillis() - startTime;
                 }
 
                 /*
                  * If we had to wait for ACK then wait for the ACK to actually
                  * get to the other side. Wait for any ACK retransmissions to
                  * finish. Then send out the request. This is a hack in support
                  * of some UA that want re-INVITEs to be spaced out in time (
                  * else they return a 400 error code ).
                  */
                 try {
                     if (timeToWait != 0) {
                         Thread.sleep(SIPDialog.this.);
                     }
                 } catch (InterruptedException ex) {
                     if (.isLoggingEnabled(.))
                         .logDebug("Interrupted sleep");
                     return;
                 }
                 if (SIPDialog.this.getState() != . && !dialogTimedOut && .getState() != . ) {
                     SIPDialog.this.sendRequest(true);
                     if (.isLoggingEnabled(.)) 
                         .logDebug(
                                 "re-INVITE successfully sent");
                 }
                
             } catch (Exception ex) {
                 .logError("Error sending re-INVITE",
                         ex);
             } finally {
                 this. = null;
             }
         }
     }
 
     class LingerTimer extends SIPStackTimerTask implements Serializable {
 
         public void runTask() {
             SIPDialog dialog = SIPDialog.this;
             .removeDialog(dialog);
             // Issue 279 :
             // https://jain-sip.dev.java.net/issues/show_bug.cgi?id=279
             // if non reentrant listener is used the event delivery of
             // DialogTerminated
             // can happen after the clean
             if (((SipStackImplgetStack()).isReEntrantListener()) {
                 cleanUp();
             }
         }
 
     }
 
     class DialogTimerTask extends SIPStackTimerTask implements Serializable {
         int nRetransmissions;
 
 
         // long cseqNumber;
 
         public DialogTimerTask(SIPServerTransaction transaction) {
             this. = transaction;
             this. = 0;
          }
 
         public void runTask() {
             // If I ACK has not been seen on Dialog,
             // resend last response.
             SIPDialog dialog = SIPDialog.this;
             if (.isLoggingEnabled(.))
                 .logDebug("Running dialog timer");
             ++;
             SIPServerTransaction transaction = this.;
             /*
              * Issue 106. Section 13.3.1.4 RFC 3261 The 2xx response is passed
              * to the transport with an interval that starts at T1 seconds and
              * doubles for each retransmission until it reaches T2 seconds If
              * the server retransmits the 2xx response for 64T1 seconds without
              * receiving an ACK, the dialog is confirmed, but the session SHOULD
              * be terminated.
              */
 
             if ( > .getAckTimeoutFactor()
                     * .) {
                 if (SIPDialog.this.getSipProvider().getSipListener() != null
                         && SIPDialog.this.getSipProvider().getSipListener() instanceof SipListenerExt) {
                     raiseErrorEvent(.);
                 } else {
                     SIPDialog.this.delete();
                 }
                 if (transaction != null
                         && transaction.getState() != ...) {
                     transaction
                             .raiseErrorEvent(.);
                 }
             } else if ((transaction != null) && (!dialog.isAckSeen())) {
                 // Retransmit to 2xx until ack receivedialog.
                 if (.intValue() / 100 == 2) {
                     try {
 
                         // resend the last response.
                         if (dialog.toRetransmitFinalResponse(transaction.T2)) {
                             transaction.resendLastResponseAsBytes();
                         }
                     } catch (IOException ex) {
 
                         raiseIOException(transaction.getPeerAddress(),
                                 transaction.getPeerPort(), transaction
                                         .getPeerProtocol());
 
                     } finally {
                         // Need to fire the timer so
                         // transaction will eventually
                         // time out whether or not
                         // the IOException occurs
                         // Note that this firing also
                         // drives Listener timeout.
                         SIPTransactionStack stack = dialog.sipStack;
                         if (.isLoggingEnabled(.)) {
                             .logDebug(
                                     "resend 200 response from " + dialog);
                         }
                         transaction.fireTimer();
                     }
                 }
             }
 
             // Stop running this timer if the dialog is in the
             // confirmed state or ack seen if retransmit filter on.
             if (dialog.isAckSeen() || dialog.dialogState == ) {
                 this. = null;
                 getStack().getTimer().cancel(this);
 
             }
 
         }
 
         @Override
         public void cleanUpBeforeCancel() {
              = null;
            // lastAckSent = null;
             cleanUpOnAck();
             super.cleanUpBeforeCancel();
         }
 
     }

    
This timer task is used to garbage collect the dialog after some time.
 
 
     class DialogDeleteTask extends SIPStackTimerTask implements Serializable {
 
         public void runTask() {
             delete();
         }
 
     }

    
This timer task is used to garbage collect the dialog after some time.
 
 
     class DialogDeleteIfNoAckSentTask extends SIPStackTimerTask implements
             Serializable {
         private long seqno;
 
         public DialogDeleteIfNoAckSentTask(long seqno) {
             this. = seqno;
         }
 
         public void runTask() {
             if (SIPDialog.this. < ) {
                 /*
                  * Did not send ACK so we need to delete the dialog. B2BUA NOTE:
                  * we may want to send BYE to the Dialog at this point. Do we
                  * want to make this behavior tailorable?
                  */
                  = null;
                 if (!SIPDialog.this.) {
                     if (.isLoggingEnabled())
                         .logError(
                                 "ACK Was not sent. killing dialog " + );
                     if (((SipProviderImpl).getSipListener() instanceof SipListenerExt) {
                         raiseErrorEvent(.);
                     } else {
                         delete();
                     }
                 } else {
                     if (.isLoggingEnabled())
                         .logError(
                                 "ACK Was not sent. Sending BYE " + );
                     if (((SipProviderImpl).getSipListener() instanceof SipListenerExt) {
                         raiseErrorEvent(.);
                     } else {
 
                         /*
                          * Send BYE to the Dialog. This will be removed for the
                          * next spec revision.
                          */
                         try {
                             Request byeRequest = SIPDialog.this
                                     .createRequest(.);
                             if (MessageFactoryImpl.getDefaultUserAgentHeader() != null) {
                                 byeRequest.addHeader(MessageFactoryImpl
                                         .getDefaultUserAgentHeader());
                             }
                             ReasonHeader reasonHeader = new Reason();
                             reasonHeader.setProtocol("SIP");
                             reasonHeader.setCause(1025);
                             reasonHeader
                                     .setText("Timed out waiting to send ACK " + );
                             byeRequest.addHeader(reasonHeader);
                             ClientTransaction byeCtx = SIPDialog.this
                                     .getSipProvider().getNewClientTransaction(
                                             byeRequest);
                             SIPDialog.this.sendRequest(byeCtx);
                             return;
                         } catch (Exception ex) {
                             SIPDialog.this.delete();
                         }
                     }
                 }
             }
         }
 
     }
 
     // ///////////////////////////////////////////////////////////
     // Constructors.
     // ///////////////////////////////////////////////////////////
     
Protected Dialog constructor.
 
     private SIPDialog(SipProviderImpl provider) {
         this. = true;
         this. = new RouteList();
         this. = // not yet initialized.
          = 0;
          = -1;
         this. = provider;
         this. = ((SIPTransactionStackprovider.getSipStack())
                 .getEarlyDialogTimeout();
     }
 
     private void recordStackTrace() {
         StringWriter stringWriter = new StringWriter();
         PrintWriter writer = new PrintWriter(stringWriter);
         new Exception().printStackTrace(writer);
         String stackTraceSignature = Integer.toString(Math.abs(new Random().nextInt()));
         .logDebug("TraceRecord = " + stackTraceSignature);
         this. = "TraceRecord = " + stackTraceSignature + ":" +  stringWriter.getBuffer().toString();
     }

    
Constructor given the first transaction.

Parameters:
transaction is the first transaction.
 
     public SIPDialog(SIPTransaction transaction) {
         this(transaction.getSipProvider());
 
         SIPRequest sipRequest = (SIPRequesttransaction.getRequest();
         this. = sipRequest.getCallId();
         this. = sipRequest.getDialogId(false);
         if (transaction == null)
             throw new NullPointerException("Null tx");
         this. = transaction.sipStack;
 
         // this.defaultRouter = new DefaultRouter((SipStack) sipStack,
         // sipStack.outboundProxy);
 
         this. = (SipProviderImpltransaction.getSipProvider();
         if ( == null)
             throw new NullPointerException("Null Provider!");
 
         this.addTransaction(transaction);
         if (.isLoggingEnabled(.)) {
             .logDebug("Creating a dialog : " + this);
             .logDebug(
                     "provider port = "
                             + this..getListeningPoint().getPort());
             .logStackTrace();
         }
         addEventListener();
     }

    
Constructor given a transaction and a response.

Parameters:
transaction -- the transaction ( client/server)
sipResponse -- response with the appropriate tags.
 
     public SIPDialog(SIPClientTransaction transactionSIPResponse sipResponse) {
         this(transaction);
         if (sipResponse == null)
             throw new NullPointerException("Null SipResponse");
         this.setLastResponse(transactionsipResponse);
     }

    
create a sip dialog with a response ( no tx)
 
     public SIPDialog(SipProviderImpl sipProviderSIPResponse sipResponse) {
         this(sipProvider);
         this. = (SIPTransactionStacksipProvider.getSipStack();
         this.setLastResponse(nullsipResponse);
         this. = sipResponse.getCSeq().getSeqNumber();
         this.setLocalTag(sipResponse.getFrom().getTag());
         this.setRemoteTag(sipResponse.getTo().getTag());
         this. = sipResponse.getFrom().getAddress();
         this. = sipResponse.getTo().getAddress();
         this. = sipResponse.getCSeq().getMethod();
         this. = sipResponse.getCallId();
         this. = false;
         if (.isLoggingEnabled(.)) {
             .logDebug("Creating a dialog : " + this);
             .logStackTrace();
         }
         addEventListener();
     }
 
     // ///////////////////////////////////////////////////////////
     // Private methods
     // ///////////////////////////////////////////////////////////
     
A debugging print routine.
 
     private void printRouteList() {
         if (.isLoggingEnabled(.)) {
             .logDebug("this : " + this);
             .logDebug(
                     "printRouteList : " + this..encode());
         }
     }

    
Raise an io exception for asyncrhonous retransmission of responses

Parameters:
host -- host to where the io was headed
port -- remote port
protocol -- protocol (udp/tcp/tls)
 
     private void raiseIOException(String hostint portString protocol) {
         // Error occured in retransmitting response.
         // Deliver the error event to the listener
         // Kill the dialog.
 
         IOExceptionEvent ioError = new IOExceptionEvent(thishostport,
                 protocol);
         .handleEvent(ioErrornull);
 
     }

    
Raise a dialog timeout if an ACK has not been sent or received

Parameters:
dialogTimeoutError
 
     private void raiseErrorEvent(int dialogTimeoutErrorSIPClientTransaction clientTransaction) {
         // Error event to send to all listeners
         SIPDialogErrorEvent newErrorEvent;
         // Iterator through the list of listeners
         Iterator<SIPDialogEventListenerlistenerIterator;
         // Next listener in the list
         SIPDialogEventListener nextListener;
 
         // Create the error event
         newErrorEvent = new SIPDialogErrorEvent(thisdialogTimeoutError);
         
         
         // The client transaction can be retrieved by the application timeout handler
         // when a re-INVITE is being sent.
         newErrorEvent.setClientTransaction(clientTransaction);
 
         // Loop through all listeners of this transaction
         synchronized () {
             
             listenerIterator = .iterator();
             while (listenerIterator.hasNext()) {
                 // Send the event to the next listener
                 nextListener = (SIPDialogEventListenerlistenerIterator.next();
                 nextListener.dialogErrorEvent(newErrorEvent);
             }
         }
         // Clear the event listeners after propagating the error.
         .clear();
         // Errors always terminate a dialog except if a timeout has occured
         // because an ACK was not sent or received, then it is the
         // responsibility of the app to terminate
         // the dialog, either by sending a BYE or by calling delete() on the
         // dialog
         if (dialogTimeoutError != .
                 && dialogTimeoutError != .
                 && dialogTimeoutError != .
                 && dialogTimeoutError != .) {
             delete();
         }
 
         // we stop the timer in any case
         stopTimer();
     }
    
    
Raise a dialog timeout if an ACK has not been sent or received

Parameters:
dialogTimeoutError
 
     private void raiseErrorEvent(int dialogTimeoutError) {
     	raiseErrorEvent(dialogTimeoutError,null);
     }

    
Set the remote party for this Dialog.

Parameters:
sipMessage -- SIP Message to extract the relevant information from.
 
     protected void setRemoteParty(SIPMessage sipMessage) {
 
         if (!isServer()) {
 
             this. = sipMessage.getTo().getAddress();
         } else {
             this. = sipMessage.getFrom().getAddress();
 
         }
         if (.isLoggingEnabled(.)) {
             .logDebug(
                     "settingRemoteParty " + this.);
         }
     }

    
Add a route list extracted from a record route list. If this is a server dialog then we assume that the record are added to the route list IN order. If this is a client dialog then we assume that the record route headers give us the route list to add in reverse order.

Parameters:
recordRouteList -- the record route list from the incoming message.
 
 
     private void addRoute(RecordRouteList recordRouteList) {
         try {
             if (!this.isServer()) {
                 // This is a client dialog so we extract the record
                 // route from the response and reverse its order to
                 // careate a route list.
                 this. = new RouteList();
                 // start at the end of the list and walk backwards
 
                 ListIterator li = recordRouteList.listIterator(recordRouteList
                         .size());
                 boolean addRoute = true;
                 while (li.hasPrevious()) {
                     RecordRoute rr = (RecordRouteli.previous();
 
                     if (addRoute) {
                         Route route = new Route();
                         AddressImpl address = ((AddressImpl) ((AddressImplrr
                                 .getAddress()).clone());
 
                         route.setAddress(address);
                         route.setParameters((NameValueListrr.getParameters()
                                 .clone());
 
                         this..add(route);
                     }
                 }
             } else {
                 // This is a server dialog. The top most record route
                 // header is the one that is closest to us. We extract the
                 // route list in the same order as the addresses in the
                 // incoming request.
                 this. = new RouteList();
                 ListIterator li = recordRouteList.listIterator();
                 boolean addRoute = true;
                 while (li.hasNext()) {
                     RecordRoute rr = (RecordRouteli.next();
 
                     if (addRoute) {
                         Route route = new Route();
                         AddressImpl address = ((AddressImpl) ((AddressImplrr
                                 .getAddress()).clone());
                         route.setAddress(address);
                         route.setParameters((NameValueListrr.getParameters()
                                 .clone());
                         .add(route);
                     }
                 }
             }
         } finally {
             if (.isLoggingEnabled()) {
                 Iterator it = .iterator();
 
                 while (it.hasNext()) {
                     SipURI sipUri = (SipURI) (((Routeit.next()).getAddress()
                             .getURI());
                     if (!sipUri.hasLrParam()) {
                         if (.isLoggingEnabled()) {
                             .logWarning(
                                     "NON LR route in Route set detected for dialog : "
                                             + this);
                             .logStackTrace();
                         }
                     } else {
                         if (.isLoggingEnabled(
                                 .))
                             .logDebug(
                                     "route = " + sipUri);
                     }
                 }
             }
         }
     }

    
Add a route list extacted from the contact list of the incoming message.

Parameters:
contactList -- contact list extracted from the incoming message.
 
 
     protected void setRemoteTarget(ContactHeader contact) {
         this. = contact.getAddress();
         if (.isLoggingEnabled(.)) {
            .logDebug(
                    "Dialog.setRemoteTarget: " + this.);
            .logStackTrace();
        }
    }

    
Extract the route information from this SIP Message and add the relevant information to the route set.

Parameters:
sipMessage is the SIP message for which we want to add the route.
    private synchronized void addRoute(SIPResponse sipResponse) {
        try {
            if (.isLoggingEnabled(.)) {
                .logDebug(
                        "setContact: dialogState: " + this + "state = "
                                + this.getState());
            }
            if (sipResponse.getStatusCode() == 100) {
                // Do nothing for trying messages.
                return;
            } else if (this. == ) {
                // Do nothing if the dialog state is terminated.
                return;
            } else if (this. == ) {
                // cannot add route list after the dialog is initialized.
                // Remote target is updated on RE-INVITE but not
                // the route list.
                if (sipResponse.getStatusCode() / 100 == 2 && !this.isServer()) {
                    ContactList contactList = sipResponse.getContactHeaders();
                    if (contactList != null
                            && SIPRequest.isTargetRefresh(sipResponse.getCSeq()
                                    .getMethod())) {
                        this.setRemoteTarget((ContactHeadercontactList
                                .getFirst());
                    }
                }
                if (!this.)
                    return;
            }
            // Update route list on response if I am a client dialog.
            if (!isServer() || this.) {
                // only update the route set if the dialog is not in the
                // confirmed state.
                if ((this.getState() != . && this
                        .getState() != .)
                        || this.) {
                    RecordRouteList rrlist = sipResponse
                            .getRecordRouteHeaders();
                    // Add the route set from the incoming response in reverse
                    // order for record route headers.
                    if (rrlist != null) {
                        this.addRoute(rrlist);
                    } else {
                        // Set the rotue list to the last seen route list.
                        this. = new RouteList();
                    }
                }
                ContactList contactList = sipResponse.getContactHeaders();
                if (contactList != null) {
                    this
                            .setRemoteTarget((ContactHeadercontactList
                                    .getFirst());
                }
            }
        } finally {
            if (.isLoggingEnabled(.)) {
                .logStackTrace();
            }
        }
    }

    
Get a cloned copy of route list for the Dialog.

Returns:
-- a cloned copy of the dialog route list.
    private synchronized RouteList getRouteList() {
            .logDebug("getRouteList " + this);
        // Find the top via in the route list.
        ListIterator li;
        RouteList retval = new RouteList();
        retval = new RouteList();
        if (this. != null) {
            li = .listIterator();
            while (li.hasNext()) {
                Route route = (Routeli.next();
                retval.add((Routeroute.clone());
            }
        }
            .logDebug("----- ");
            .logDebug("getRouteList for " + this);
            if (retval != null)
                .logDebug(
                        "RouteList = " + retval.encode());
            if ( != null)
                .logDebug(
                        "myRouteList = " + .encode());
            .logDebug("----- ");
        }
        return retval;
    }
    void setRouteList(RouteList routeList) {
        this. = routeList;
    }

    
Sends ACK Request to the remote party of this Dialogue.

Parameters:
request the new ACK Request message to send.
throwIOExceptionAsSipException - throws SipException if IOEx encountered. Otherwise, no exception is propagated.
releaseAckSem - release ack semaphore.
Throws:
javax.sip.SipException if implementation cannot send the ACK Request for any other reason
    private void sendAck(Request requestboolean throwIOExceptionAsSipException)
            throws SipException {
        SIPRequest ackRequest = (SIPRequestrequest;
            .logDebug("sendAck" + this);
        
        if (!ackRequest.getMethod().equals(.))
            throw new SipException("Bad request method -- should be ACK");
        if (this.getState() == null
                || this.getState().getValue() == ) {
            if (.isLoggingEnabled(.)) {
                .logError(
                        "Bad Dialog State for " + this + " dialogID = "
                                + this.getDialogId());
            }
            throw new SipException("Bad dialog state " + this.getState());
        }
        if (!this.getCallId().getCallId().equals(
                ((SIPRequestrequest).getCallId().getCallId())) {
            if (.isLoggingEnabled(.)) {
                
                        .