Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Conditions Of Use 
    * 
    * This software was developed by employees of the National Institute of
    * Standards and Technology (NIST), an agency of the Federal Government.
    * Pursuant to title 15 Untied States Code Section 105, works of NIST
    * employees are not subject to copyright protection in the United States
    * and are considered to be in the public domain.  As a result, a formal
    * license is not needed to use the software.
   * 
   * This software is provided by NIST as a service and is expressly
   * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
   * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
   * AND DATA ACCURACY.  NIST does not warrant or make any representations
   * regarding the use of the software or the results thereof, including but
   * not limited to the correctness, accuracy, reliability or usefulness of
   * the software.
   * 
   * Permission to use this software is contingent upon your acceptance
   * of the terms of this agreement
   *  
   * .
   * 
   */
  package gov.nist.javax.sip.stack;
  
  
  import java.util.HashSet;
  import java.util.Set;
  
  import javax.sip.Dialog;
  import javax.sip.Timeout;
  
  /*
   * Jeff Keyser -- initial. Daniel J. Martinez Manzano --Added support for TLS message channel.
   * Emil Ivov -- bug fixes. Chris Beardshear -- bug fix. Andreas Bystrom -- bug fixes. Matt Keller
   * (Motorolla) -- bug fix.
   */

Represents a client transaction. Implements the following state machines. (From RFC 3261)
                   
                    
                     
                      
                      
                      
                                                     |INVITE from TU
                                   Timer A fires     |INVITE sent
                                   Reset A,          V                      Timer B fires
                                   INVITE sent +-----------+                or Transport Err.
                                     +---------|           |---------------+inform TU
                                     |         |  Calling  |               |
                                     +-------->|           |-------------->|
                                               +-----------+ 2xx           |
                                                  |  |       2xx to TU     |
                                                  |  |1xx                  |
                          300-699 +---------------+  |1xx to TU            |
                         ACK sent |                  |                     |
                      resp. to TU |  1xx             V                     |
                                  |  1xx to TU  -----------+               |
                                  |  +---------|           |               |
                                  |  |         |Proceeding |-------------->|
                                  |  +-------->|           | 2xx           |
                                  |            +-----------+ 2xx to TU     |
                                  |       300-699    |                     |
                                  |       ACK sent,  |                     |
                                  |       resp. to TU|                     |
                                  |                  |                     |      NOTE:
                                  |  300-699         V                     |
                                  |  ACK sent  +-----------+Transport Err. |  transitions
                                  |  +---------|           |Inform TU      |  labeled with
                                  |  |         | Completed |-------------->|  the event
                                  |  +-------->|           |               |  over the action
                                  |            +-----------+               |  to take
                                  |              ˆ   |                     |
                                  |              |   | Timer D fires       |
                                  +--------------+   | -                   |
                                                     |                     |
                                                     V                     |
                                               +-----------+               |
                                               |           |               |
                                               | Terminated|<--------------+
                                               |           |
                                               +-----------+
                      
                                       Figure 5: INVITE client transaction
                      
                      
                                                         |Request from TU
                                                         |send request
                                     Timer E             V
                                     send request  +-----------+
                                         +---------|           |-------------------+
                                         |         |  Trying   |  Timer F          |
                                         +-------->|           |  or Transport Err.|
                                                   +-----------+  inform TU        |
                                      200-699         |  |                         |
                                      resp. to TU     |  |1xx                      |
                                      +---------------+  |resp. to TU              |
                                      |                  |                         |
                                      |   Timer E        V       Timer F           |
                                      |   send req +-----------+ or Transport Err. |
                                      |  +---------|           | inform TU         |
                                      |  |         |Proceeding |------------------>|
                                      |  +-------->|           |-----+             |
                                      |            +-----------+     |1xx          |
                                      |              |      ˆ        |resp to TU   |
                                      | 200-699      |      +--------+             |
                                      | resp. to TU  |                             |
                                      |              |                             |
                                      |              V                             |
                                      |            +-----------+                   |
                                      |            |           |                   |
                                      |            | Completed |                   |
                                      |            |           |                   |
                                      |            +-----------+                   |
                                      |              ˆ   |                         |
                                      |              |   | Timer K                 |
                                      +--------------+   | -                       |
                                                         |                         |
                                                         V                         |
                                   NOTE:           +-----------+                   |
                                                   |           |                   |
                               transitions         | Terminated|<------------------+
                               labeled with        |           |
                               the event           +-----------+
                               over the action
                               to take
                      
                                       Figure 6: non-INVITE client transaction
                      
                      
                      
                      
                     
                    
 

Author(s):
M. Ranganathan
Version:
1.2 $Revision: 1.143 $ $Date: 2010/10/15 10:19:49 $
 
 public class SIPClientTransaction extends SIPTransaction implements ServerResponseInterface,
 
     // a SIP Client transaction may belong simultaneously to multiple
     // dialogs in the early state. These dialogs all have
     // the same call ID and same From tag but different to tags.
     
     //jeand : we don't keep the ref to the dialogs but only to their id to save on memory
     private Set<StringsipDialogs;
 
     private SIPRequest lastRequest;
 
     private int viaPort;
 
     private String viaHost;
 
     // Real ResponseInterface to pass messages to
     private transient ServerResponseInterface respondTo;
 
     // jeand: ref to the default dialog id to allow nullying the ref to the dialog quickly
     // and thus saving on mem
     private String defaultDialogId;
     private SIPDialog defaultDialog;
 
     private Hop nextHop;
 
     private boolean notifyOnRetransmit;
 
     private boolean timeoutIfStillInCallingState;
 
     private int callingStateTimeoutCount;
     
     
     // jeand/ avoid keeping the full Original Request in memory
 	
 	private Object transactionTimerLock = new Object();
 	private AtomicBoolean timerKStarted = new AtomicBoolean(false);
 	private boolean transactionTimerCancelled = false;
 	private Set<IntegerresponsesReceived = new HashSet<Integer>(2);
 	
 
     public class TransactionTimer extends SIPStackTimerTask {
 
         public TransactionTimer() {
 
         }
 
         public void runTask() {
             SIPClientTransaction clientTransaction;
             SIPTransactionStack sipStack;
             clientTransaction = SIPClientTransaction.this;
             sipStack = clientTransaction.sipStack;
 
             // If the transaction has terminated,
             if (clientTransaction.isTerminated()) {                             
 
                 try {
                 	sipStack.getTimer().cancel(this);
 
                 } catch (IllegalStateException ex) {
                     if (!sipStack.isAlive())
                         return;
                 }
 
                 cleanUpOnTerminated();
 
             } else {
                 // If this transaction has not
                 // terminated,
                 // Fire the transaction timer.
                 clientTransaction.fireTimer();
                                 
             }
 
         }
 
     }
     
     class ExpiresTimerTask extends SIPStackTimerTask {
         
         public ExpiresTimerTask() {
             
         }
 
         @Override
         public void runTask() {
             SIPClientTransaction ct = SIPClientTransaction.this;
             SipProviderImpl provider = ct.getSipProvider();
      
             if (ct.getState() != . ) {
                 TimeoutEvent tte = new TimeoutEvent(SIPClientTransaction.this.getSipProvider(), 
                         SIPClientTransaction.this.);
                 provider.handleEvent(ttect);
             } else {
                 if ( SIPClientTransaction.this.getSIPStack().getStackLogger().isLoggingEnabled(.) ) {
                     SIPClientTransaction.this.getSIPStack().getStackLogger().logDebug("state = " + ct.getState());
                 }
             }
         }
         
     }

    
Creates a new client transaction.

Parameters:
newSIPStack Transaction stack this transaction belongs to.
newChannelToUse Channel to encapsulate.
Returns:
the created client transaction.
 
     protected SIPClientTransaction(SIPTransactionStack newSIPStackMessageChannel newChannelToUse) {
         super(newSIPStacknewChannelToUse);
         // Create a random branch parameter for this transaction
         setBranch(Utils.getInstance().generateBranchId());
         this. = newChannelToUse.messageProcessor;
         this.setEncapsulatedChannel(newChannelToUse);
         this. = false;
         this. = false;
 
             .getStackLogger().logDebug("Creating clientTransaction " + this);
             .getStackLogger().logStackTrace();
         }
         // this.startTransactionTimer();
         this. = new CopyOnWriteArraySet<String>();
     }

    
Sets the real ResponseInterface this transaction encapsulates.

Parameters:
newRespondTo ResponseInterface to send messages to.
 
     public void setResponseInterface(ServerResponseInterface newRespondTo) {
             .getStackLogger().logDebug(
                     "Setting response interface for " + this + " to " + newRespondTo);
             if (newRespondTo == null) {
                 .getStackLogger().logStackTrace();
                 .getStackLogger().logDebug("WARNING -- setting to null!");
             }
         }
 
          = newRespondTo;
 
     }

    
Returns this transaction.
 
     public MessageChannel getRequestChannel() {
 
         return this;
 
     }

    
Deterines if the message is a part of this transaction.

Parameters:
messageToTest Message to check if it is part of this transaction.
Returns:
true if the message is part of this transaction, false if not.
 
     public boolean isMessagePartOfTransaction(SIPMessage messageToTest) {
 
         // List of Via headers in the message to test
         Via topMostViaHeader = messageToTest.getTopmostVia();
         // Flags whether the select message is part of this transaction
         boolean transactionMatches;
         String messageBranch = topMostViaHeader.getBranch();
         boolean rfc3261Compliant = getBranch() != null
                 && messageBranch != null
                 && getBranch().toLowerCase().startsWith(
                         .)
                 && messageBranch.toLowerCase().startsWith(
                         .);
 
         transactionMatches = false;
         if (. == this.getInternalState()) {
             if (rfc3261Compliant) {
                 transactionMatches = getBranch().equalsIgnoreCase(
                 		topMostViaHeader.getBranch())
                         && getMethod().equals(messageToTest.getCSeq().getMethod());
             } else {
                 transactionMatches = getBranch().equals(messageToTest.getTransactionId());
             }
         } else if (!isTerminated()) {
             if (rfc3261Compliant) {
                 if (topMostViaHeader != null) {
                     // If the branch parameter is the
                     // same as this transaction and the method is the same,
                     if (getBranch().equalsIgnoreCase(topMostViaHeader.getBranch())) {
                         transactionMatches = getMethod().equals(
                                 messageToTest.getCSeq().getMethod());
 
                     }
                 }
             } else {
                 // not RFC 3261 compliant.
                 if (getBranch() != null) {
                     transactionMatches = getBranch().equalsIgnoreCase(
                             messageToTest.getTransactionId());
                 } else {
                     transactionMatches = ((SIPRequest)getRequest()).getTransactionId()
                             .equalsIgnoreCase(messageToTest.getTransactionId());
                 }
 
             }
 
         }
         return transactionMatches;
 
     }

    
Send a request message through this transaction and onto the client.

Parameters:
messageToSend Request to process and send.
 
     public void sendMessage(SIPMessage messageToSendthrows IOException {
 
         try {
             // Message typecast as a request
             SIPRequest transactionRequest;
 
             transactionRequest = (SIPRequestmessageToSend;
 
             // Set the branch id for the top via header.
             Via topVia = (ViatransactionRequest.getTopmostVia();
             // Tack on a branch identifier to match responses.
             try {
                 topVia.setBranch(getBranch());
             } catch (java.text.ParseException ex) {
             }
 
             if (.isLoggingEnabled(.)) {
                 .getStackLogger().logDebug("Sending Message " + messageToSend);
                 .getStackLogger().logDebug("TransactionState " + this.getState());
             }
             // If this is the first request for this transaction,
             if (. == getInternalState()
                     || . == getInternalState()) {
 
                 // If this is a TU-generated ACK request,
                 if (transactionRequest.getMethod().equals(.)) {
 
                     // Send directly to the underlying
                     // transport and close this transaction
                     if (isReliable()) {
                         this.setState(.);
                     } else {
                         this.setState(.);
                     }
                     cleanUpOnTimer();
                     // BUGBUG -- This suppresses sending the ACK uncomment this
                     // to
                     // test 4xx retransmission
                     // if (transactionRequest.getMethod() != Request.ACK)
                     super.sendMessage(transactionRequest);
                     return;
 
                 }
 
             }
             try {
 
                 // Send the message to the server
                  = transactionRequest;
                 if (getInternalState() < 0) {
                     // Save this request as the one this transaction
                     // is handling
                     setOriginalRequest(transactionRequest);
                     // Change to trying/calling state
                     // Set state first to avoid race condition..
 
                     if (transactionRequest.getMethod().equals(.)) {
                         this.setState(.);
                     } else if (transactionRequest.getMethod().equals(.)) {
                         // Acks are never retransmitted.
                         this.setState(.);
                         cleanUpOnTimer();
                     } else {
                         this.setState(.);
                     }
                     if (!isReliable()) {
                         enableRetransmissionTimer();
                     }
                     if (isInviteTransaction()) {
                         enableTimeoutTimer();
                     } else {
                         enableTimeoutTimer();
                     }
                 }
                 // BUGBUG This supresses sending ACKS -- uncomment to test
                 // 4xx retransmission.
                 // if (transactionRequest.getMethod() != Request.ACK)
                 super.sendMessage(transactionRequest);
 
             } catch (IOException e) {
 
                 this.setState(.);
                 throw e;
 
             }
         } finally {
             this. = true;
             this.startTransactionTimer();
 
         }
 
     }

    
Process a new response message through this transaction. If necessary, this message will also be passed onto the TU.

Parameters:
transactionResponse Response to process.
sourceChannel Channel that received this message.
 
     public synchronized void processResponse(SIPResponse transactionResponse,
             MessageChannel sourceChannelSIPDialog dialog) {
 
         // If the state has not yet been assigned then this is a
         // spurious response.
 
         if (getInternalState() < 0)
             return;
 
         // Ignore 1xx
         if ((. == this.getInternalState() || . == this
                 .getInternalState())
                 && transactionResponse.getStatusCode() / 100 == 1) {
             return;
         }
 
             .getStackLogger().logDebug(
                     "processing " + transactionResponse.getFirstLine() + "current state = "
                             + getState());
             .getStackLogger().logDebug("dialog = " + dialog);
         }
 
         this. = transactionResponse;
 
         /*
          * JvB: this is now duplicate with code in the other processResponse
          * 
          * if (dialog != null && transactionResponse.getStatusCode() != 100 &&
          * (transactionResponse.getTo().getTag() != null || sipStack .isRfc2543Supported())) { //
          * add the route before you process the response. dialog.setLastResponse(this,
          * transactionResponse); this.setDialog(dialog, transactionResponse.getDialogId(false)); }
          */
 
         try {
             if (isInviteTransaction())
                 inviteClientTransaction(transactionResponsesourceChanneldialog);
             else
                 nonInviteClientTransaction(transactionResponsesourceChanneldialog);
         } catch (IOException ex) {
             if (.isLoggingEnabled())
                 .getStackLogger().logException(ex);
             this.setState(.);
         }
     }

    
Implements the state machine for invite client transactions.
                   
                    
                     
                      
                      
                                                         |Request from TU
                                                         |send request
                                     Timer E             V
                                     send request  +-----------+
                                         +---------|           |-------------------+
                                         |         |  Trying   |  Timer F          |
                                         +-------->|           |  or Transport Err.|
                                                   +-----------+  inform TU        |
                                      200-699         |  |                         |
                                      resp. to TU     |  |1xx                      |
                                      +---------------+  |resp. to TU              |
                                      |                  |                         |
                                      |   Timer E        V       Timer F           |
                                      |   send req +-----------+ or Transport Err. |
                                      |  +---------|           | inform TU         |
                                      |  |         |Proceeding |------------------>|
                                      |  +-------->|           |-----+             |
                                      |            +-----------+     |1xx          |
                                      |              |      ˆ        |resp to TU   |
                                      | 200-699      |      +--------+             |
                                      | resp. to TU  |                             |
                                      |              |                             |
                                      |              V                             |
                                      |            +-----------+                   |
                                      |            |           |                   |
                                      |            | Completed |                   |
                                      |            |           |                   |
                                      |            +-----------+                   |
                                      |              ˆ   |                         |
                                      |              |   | Timer K                 |
                                      +--------------+   | -                       |
                                                         |                         |
                                                         V                         |
                                   NOTE:           +-----------+                   |
                                                   |           |                   |
                               transitions         | Terminated|<------------------+
                               labeled with        |           |
                               the event           +-----------+
                               over the action
                               to take
                      
                                       Figure 6: non-INVITE client transaction
                      
                      
                     
                    
 

Parameters:
transactionResponse -- transaction response received.
sourceChannel - source channel on which the response was received.
 
     private void nonInviteClientTransaction(SIPResponse transactionResponse,
             MessageChannel sourceChannelSIPDialog sipDialogthrows IOException {
         int statusCode = transactionResponse.getStatusCode();
         if (. == this.getInternalState()) {
             if (statusCode / 100 == 1) {                
                 this.setState(.);
                 enableTimeoutTimer();
                 // According to RFC, the TU has to be informed on
                 // this transition.
                 if ( != null) {
                     .processResponse(transactionResponsethissipDialog);
                 } else {
                     this.semRelease();
                 }
             } else if (200 <= statusCode && statusCode <= 699) {
                 if (!isReliable()) {                    
                     this.setState(.);
                     scheduleTimerK();        
                 } else {                    
                     this.setState(.);
                 }
                 // Send the response up to the TU.
                 if ( != null) {
                     .processResponse(transactionResponsethissipDialog);
                 } else {
                     this.semRelease();
                 }
                 if (isReliable()
                     && . == getInternalState()) {
                     cleanUpOnTerminated();
                 }
                 cleanUpOnTimer();
             }
         } else if (. == this.getInternalState()) {
             if (statusCode / 100 == 1) {
                 if ( != null) {
                     .processResponse(transactionResponsethissipDialog);
                 } else {
                     this.semRelease();
                 }
             } else if (200 <= statusCode && statusCode <= 699) {
                 disableRetransmissionTimer();
                 disableTimeoutTimer();
                 if (!isReliable()) {                    
                     this.setState(.);
                     scheduleTimerK();
                 } else {                                        
                     this.setState(.);
                 }
                 if ( != null) {
                     .processResponse(transactionResponsethissipDialog);
                 } else {
                     this.semRelease();
                 }
                 if (isReliable()
                     && . == getInternalState()) {
                     cleanUpOnTerminated();
                 }
                 cleanUpOnTimer();
             }
         } else {
             if (.isLoggingEnabled(.)) {
                 .getStackLogger().logDebug(
                         " Not sending response to TU! " + getState());
             }
             this.semRelease();
         }
     }    
 
     // avoid re-scheduling the transaction timer every 500ms while we know we have to wait for TIMER_K * 500 ms
 	private void scheduleTimerK(long time) {
 		if( != null &&  .compareAndSet(falsetrue)) {
 			synchronized () {
 					 = null;
                         .getStackLogger().logDebug("starting TransactionTimerK() : " + getTransactionId() + " time " + time);
                     }
 					SIPStackTimerTask task = new SIPStackTimerTask () {                         
                         
                         public void runTask() {
                             if (.isLoggingEnabled(.)) {
                                 .getStackLogger().logDebug("executing TransactionTimerJ() : " + getTransactionId());
                             }
                             fireTimeoutTimer();                                  
                             cleanUpOnTerminated();
                         }
                     };
                     if(time > 0) {
                         .getTimer().schedule(tasktime * );
                     } else {
                         task.runTask();
                     }
 				}
 			}        	        	
         }            
 	}

Implements the state machine for invite client transactions.
                   
                    
                     
                      
                      
                                                     |INVITE from TU
                                   Timer A fires     |INVITE sent
                                   Reset A,          V                      Timer B fires
                                   INVITE sent +-----------+                or Transport Err.
                                     +---------|           |---------------+inform TU
                                     |         |  Calling  |               |
                                     +-------->|           |-------------->|
                                               +-----------+ 2xx           |
                                                  |  |       2xx to TU     |
                                                  |  |1xx                  |
                          300-699 +---------------+  |1xx to TU            |
                         ACK sent |                  |                     |
                      resp. to TU |  1xx             V                     |
                                  |  1xx to TU  -----------+               |
                                  |  +---------|           |               |
                                  |  |         |Proceeding |-------------->|
                                  |  +-------->|           | 2xx           |
                                  |            +-----------+ 2xx to TU     |
                                  |       300-699    |                     |
                                  |       ACK sent,  |                     |
                                  |       resp. to TU|                     |
                                  |                  |                     |      NOTE:
                                  |  300-699         V                     |
                                  |  ACK sent  +-----------+Transport Err. |  transitions
                                  |  +---------|           |Inform TU      |  labeled with
                                  |  |         | Completed |-------------->|  the event
                                  |  +-------->|           |               |  over the action
                                  |            +-----------+               |  to take
                                  |              ˆ   |                     |
                                  |              |   | Timer D fires       |
                                  +--------------+   | -                   |
                                                     |                     |
                                                     V                     |
                                               +-----------+               |
                                               |           |               |
                                               | Terminated|<--------------+
                                               |           |
                                               +-----------+
                      
                      
                     
                    
 

Parameters:
transactionResponse -- transaction response received.
sourceChannel - source channel on which the response was received.
 
 
     private void inviteClientTransaction(SIPResponse transactionResponse,
             MessageChannel sourceChannelSIPDialog dialogthrows IOException {
         int statusCode = transactionResponse.getStatusCode();
        
         if (. == this.getInternalState()) {
             boolean ackAlreadySent = false;
            // if (dialog != null  && dialog.isAckSeen() && dialog.getLastAckSent() != null) 
             if ( dialog!= null && dialog.isAckSent(transactionResponse.getCSeq().getSeqNumber())) {
                 if (dialog.getLastAckSent().getCSeq().getSeqNumber() == transactionResponse.getCSeq()
                         .getSeqNumber()
                         && transactionResponse.getFromTag().equals(
                                 dialog.getLastAckSent().getFromTag())) {
                     // the last ack sent corresponded to this response
                     ackAlreadySent = true;
                 }
             }
             // retransmit the ACK for this response.
             if (dialog!= null && ackAlreadySent
                     && transactionResponse.getCSeq().getMethod().equals(dialog.getMethod())) {
                 try {
                     // Found the dialog - resend the ACK and
                     // dont pass up the null transaction
                     if (.isLoggingEnabled(.))
                         .getStackLogger().logDebug("resending ACK");
 
                     dialog.resendAck();
                 } catch (SipException ex) {
                     // What to do here ?? kill the dialog?
                 }
             }
 
             this.semRelease();
             return;
         } else if (. == this.getInternalState()) {
             if (statusCode / 100 == 2) {
 
                 // JvB: do this ~before~ calling the application, to avoid
                 // retransmissions
                 // of the INVITE after app sends ACK
                 disableRetransmissionTimer();
                 disableTimeoutTimer();                
                 this.setState(.);
 
                 // 200 responses are always seen by TU.
                 if ( != null)
                     .processResponse(transactionResponsethisdialog);
                 else {
                     this.semRelease();
                 }
 
             } else if (statusCode / 100 == 1) {
                 disableRetransmissionTimer();
                 disableTimeoutTimer();                
                 this.setState(.);
 
                 if ( != null)
                     .processResponse(transactionResponsethisdialog);
                 else {
                     this.semRelease();
                 }
 
             } else if (300 <= statusCode && statusCode <= 699) {
                 // Send back an ACK request
 
                 try {
                     sendMessage((SIPRequestcreateErrorAck());
 
                 } catch (Exception ex) {
                     .getStackLogger().logError(
                             "Unexpected Exception sending ACK -- sending error AcK "ex);
 
                 }
 
                 /*
                  * When in either the "Calling" or "Proceeding" states, reception of response with
                  * status code from 300-699 MUST cause the client transaction to transition to
                  * "Completed". The client transaction MUST pass the received response up to the
                  * TU, and the client transaction MUST generate an ACK request.
                  */
 
                 if (this.getDialog() != null &&  ((SIPDialog)this.getDialog()).isBackToBackUserAgent()) {
                     ((SIPDialogthis.getDialog()).releaseAckSem();
                 }
 
                 if (!isReliable()) {                    
                     this.setState(.);
                     enableTimeoutTimer();
                 } else {                    
                     // Proceed immediately to the TERMINATED state.
                     this.setState(.);
                 }
                 if ( != null) {
                     .processResponse(transactionResponsethisdialog);
                 } else {
                     this.semRelease();
                 }
                 cleanUpOnTimer();
             }
         } else if (. == this.getInternalState()) {
             if (statusCode / 100 == 1) {
                 if ( != null) {
                     .processResponse(transactionResponsethisdialog);
                 } else {
                     this.semRelease();
                 }
             } else if (statusCode / 100 == 2) {                
                 this.setState(.);
                 if ( != null) {
                     .processResponse(transactionResponsethisdialog);
                 } else {
                     this.semRelease();
                 }
 
             } else if (300 <= statusCode && statusCode <= 699) {
                 // Send back an ACK request
                 try {
                     sendMessage((SIPRequestcreateErrorAck());
                 } catch (Exception ex) {
                     InternalErrorHandler.handleException(ex);
                 }
 
                 if (this.getDialog() != null) {
                     ((SIPDialogthis.getDialog()).releaseAckSem();
                 }
                 // JvB: update state before passing to app
                 if (!isReliable()) {                    
                     this.setState(.);
                     this.enableTimeoutTimer();
                 } else {                    
                     this.setState(.);
                 }
                 cleanUpOnTimer();
 
                 // Pass up to the TU for processing.
                 if ( != null)
                     .processResponse(transactionResponsethisdialog);
                 else {
                     this.semRelease();
                 }
 
                 // JvB: duplicate with line 874
                 // if (!isReliable()) {
                 // enableTimeoutTimer(TIMER_D);
                 // }
             }
         } else if (. == this.getInternalState()) {
             if (300 <= statusCode && statusCode <= 699) {
                 // Send back an ACK request
                 try {
                     sendMessage((SIPRequestcreateErrorAck());
                 } catch (Exception ex) {
                     InternalErrorHandler.handleException(ex);
                 } finally {
                     this.semRelease();
                 }
             }
 
         }
 
     }
 
     /*
      * (non-Javadoc)
      * 
      * @see javax.sip.ClientTransaction#sendRequest()
      */
     public void sendRequest() throws SipException {
         SIPRequest sipRequest = this.getOriginalRequest();
 
         if (this.getInternalState() >= 0)
             throw new SipException("Request already sent");
 
             .getStackLogger().logDebug("sendRequest() " + sipRequest);
         }
 
         try {
             sipRequest.checkHeaders();
         } catch (ParseException ex) {
         	if (.isLoggingEnabled())
         		.getStackLogger().logError("missing required header");
             throw new SipException(ex.getMessage());
         }
 
         if (getMethod().equals(.)
                 && sipRequest.getHeader(.) == null) {
             /*
              * If no "Expires" header is present in a SUBSCRIBE request, the implied default is
              * defined by the event package being used.
              * 
              */
         	if (.isLoggingEnabled())
         		.getStackLogger().logWarning(
                     "Expires header missing in outgoing subscribe --"
                             + " Notifier will assume implied value on event package");
         }
         try {
             /*
              * This check is removed because it causes problems for load balancers ( See issue
              * 136) reported by Raghav Ramesh ( BT )
              * 
              */
             if (this.getMethod().equals(.)
                     && .isCancelClientTransactionChecked()) {
                 SIPClientTransaction ct = (SIPClientTransaction.findCancelTransaction(
                         this.getOriginalRequest(), false);
                 if (ct == null) {
                     /*
                      * If the original request has generated a final response, the CANCEL SHOULD
                      * NOT be sent, as it is an effective no-op, since CANCEL has no effect on
                      * requests that have already generated a final response.
                      */
                     throw new SipException("Could not find original tx to cancel. RFC 3261 9.1");
                 } else if (ct.getInternalState() < 0) {
                     throw new SipException(
                             "State is null no provisional response yet -- cannot cancel RFC 3261 9.1");
                 } else if (!ct.isInviteTransaction()) {
                     throw new SipException("Cannot cancel non-invite requests RFC 3261 9.1");
                 }
             } else if (this.getMethod().equals(.)
                     || this.getMethod().equals(.)) {
                 SIPDialog dialog = .getDialog(this.getOriginalRequest()
                         .getDialogId(false));
                 // I want to behave like a user agent so send the BYE using the
                 // Dialog
                if (this.getSipProvider().isAutomaticDialogSupportEnabled() && dialog != null) {
                    throw new SipException(
                            "Dialog is present and AutomaticDialogSupport is enabled for "
                                    + " the provider -- Send the Request using the Dialog.sendRequest(transaction)");
                }
            }
            // Only map this after the fist request is sent out.
            if (isInviteTransaction()) {
                SIPDialog dialog = this.getDefaultDialog();
                if (dialog != null && dialog.isBackToBackUserAgent()) {
                    // Block sending re-INVITE till we see the ACK.
                    if ( ! dialog.takeAckSem() ) {
                        throw new SipException ("Failed to take ACK semaphore");
                    }
                }
            }
            this. = true;
         // Time extracted from the Expires header.
            int expiresTime = -1;
           if ( sipRequest.getHeader(.) != null ) {
                Expires expires = (ExpiressipRequest.getHeader(.);
                expiresTime = expires.getExpires();
            } 
            // This is a User Agent. The user has specified an Expires time. Start a timer
            // which will check if the tx is terminated by that time.
            if ( this.getDefaultDialog() != null  &&  isInviteTransaction() &&
                    expiresTime != -1 &&  == null ) {
                this. = new ExpiresTimerTask();
                .getTimer().schedule(expiresTime * 1000);
                
            }
            this.sendMessage(sipRequest);
            
        } catch (IOException ex) {
            this.setState(.);
            if ( this. != null ) {
                .getTimer().cancel(this.);
            }
            throw new SipException(
                    ex.getMessage() == null ? "IO Error sending request" : ex.getMessage(),
                    ex);
        }
    }

    
Called by the transaction stack when a retransmission timer fires.
    protected void fireRetransmissionTimer() {
        try {
            // Resend the last request sent
            if (this.getInternalState() < 0 || !this.)
                return;
            boolean inv = isInviteTransaction();
            int s = this.getInternalState();
            // JvB: INVITE CTs only retransmit in CALLING, non-INVITE in both TRYING and
            // PROCEEDING
            // Bug-fix for non-INVITE transactions not retransmitted when 1xx response received
            if ((inv && . == s)
                    || (!inv && (. == s || . == s))) {
                // If the retransmission filter is disabled then
                // retransmission of the INVITE is the application
                // responsibility.
                if ( != null) {
                    if (.
                            && .getHeader(.) != null) {
                        long milisec = System.currentTimeMillis();
                        TimeStamp timeStamp = new TimeStamp();
                        try {
                            timeStamp.setTimeStamp(milisec);
                        } catch (InvalidArgumentException ex) {
                            InternalErrorHandler.handleException(ex);
                        }
                        .setHeader(timeStamp);
                    }
                    super.sendMessage();
                    if (this.) {
                        TimeoutEvent txTimeout = new TimeoutEvent(this.getSipProvider(), this,
                                .);
                        this.getSipProvider().handleEvent(txTimeoutthis);
                    }
                    if (this.
                            && this.getInternalState() == .) {
                        this.--;
                        if ( == 0) {
                            TimeoutEvent timeoutEvent = new TimeoutEvent(this.getSipProvider(),
                                    this.);
                            this.getSipProvider().handleEvent(timeoutEventthis);
                            this. = false;
                        }
                    }
                }
            }
        } catch (IOException e) {
            this.raiseIOExceptionEvent();
        }
    }

    
Called by the transaction stack when a timeout timer fires.
    protected void fireTimeoutTimer() {
            .getStackLogger().logDebug("fireTimeoutTimer " + this);
        SIPDialog dialog = (SIPDialogthis.getDialog();
        if (. == this.getInternalState()
                || . == this.getInternalState()
                || . == this.getInternalState()) {
            // Timeout occured. If this is asociated with a transaction
            // creation then kill the dialog.
            if (dialog != null
                    && (dialog.getState() == null || dialog.getState() == .)) {
                if (SIPTransactionStack.isDialogCreated(this.getMethod())) {
                    // If this is a re-invite we do not delete the dialog even
                    // if the
                    // reinvite times out. Else
                    // terminate the enclosing dialog.
                    dialog.delete();
                }
            } else if (dialog != null) {
                // Guard against the case of BYE time out.
                if (this.getMethod().equalsIgnoreCase(.)
                        && dialog.isTerminatedOnBye()) {
                    // Terminate the associated dialog on BYE Timeout.
                    dialog.delete();
                }
            }
        }
            // Got a timeout error on a cancel.
            if (this.getMethod().equalsIgnoreCase(.)) {
                SIPClientTransaction inviteTx = (SIPClientTransactionthis.getOriginalRequest()
                        .getInviteTransaction();
                if (inviteTx != null
                        && ((inviteTx.getInternalState() == . || inviteTx
                                .getInternalState() == .))
                        && inviteTx.getDialog() != null) {
                    /*
                     * A proxy server should have started TIMER C and take care of the Termination
                     * using transaction.terminate() by itself (i.e. this is not the job of the
                     * stack at this point but we do it to be nice.
                     */
                    inviteTx.setState(.);
                }
            }
        } else {
            this.setState(.);
        }
    }
    /*
     * (non-Javadoc)
     * 
     * @see javax.sip.ClientTransaction#createCancel()
     */
    public Request createCancel() throws SipException {
        SIPRequest originalRequest = this.getOriginalRequest();
        if (originalRequest == null)
            throw new SipException("Bad state " + getState());
        if (!originalRequest.getMethod().equals(.))
            throw new SipException("Only INIVTE may be cancelled");
        if (originalRequest.getMethod().equalsIgnoreCase(.))
            throw new SipException("Cannot Cancel ACK!");
        else {
            SIPRequest cancelRequest = originalRequest.createCancelRequest();
            cancelRequest.setInviteTransaction(this);
            return cancelRequest;
        }
    }
    /*
     * (non-Javadoc)
     * 
     * @see javax.sip.ClientTransaction#createAck()
     */
    public Request createAck() throws SipException {
        SIPRequest originalRequest = this.getOriginalRequest();
        if (originalRequest == null)
            throw new SipException("bad state " + getState());
        if (getMethod().equalsIgnoreCase(.)) {
            throw new SipException("Cannot ACK an ACK!");
        } else if ( == null) {
            throw new SipException("bad Transaction state");
        } else if (.getStatusCode() < 200) {
            if (.isLoggingEnabled(.)) {
                .getStackLogger().logDebug("lastResponse = " + );
            }
            throw new SipException("Cannot ACK a provisional response!");
        }
        SIPRequest ackRequest = originalRequest.createAckRequest((To.getTo());
        // Pull the record route headers from the last reesponse.
        RecordRouteList recordRouteList = .getRecordRouteHeaders();
        if (recordRouteList == null) {
            // If the record route list is null then we can
            // construct the ACK from the specified contact header.
            // Note the 3xx check here because 3xx is a redirect.
            // The contact header for the 3xx is the redirected
            // location so we cannot use that to construct the
            // request URI.
            if (.getContactHeaders() != null
                    && .getStatusCode() / 100 != 3) {
                Contact contact = (Contact.getContactHeaders().getFirst();
                javax.sip.address.URI uri = (javax.sip.address.URIcontact.getAddress().getURI()
                        .clone();
                ackRequest.setRequestURI(uri);
            }
            return ackRequest;
        }
        ackRequest.removeHeader(.);
        RouteList routeList = new RouteList();
        // start at the end of the list and walk backwards
        ListIterator<RecordRouteli = recordRouteList.listIterator(recordRouteList.size());
        while (li.hasPrevious()) {
            RecordRoute rr = (RecordRouteli.previous();
            Route route = new Route();
            route.setAddress((AddressImpl) ((AddressImplrr.getAddress()).clone());
            route.setParameters((NameValueListrr.getParameters().clone());
            routeList.add(route);
        }
        Contact contact = null;
        if (.getContactHeaders() != null) {
            contact = (Contact.getContactHeaders().getFirst();
        }
        if (!((