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 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.121 $ $Date: 2009/12/06 15:58:39 $
 
 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.
 
 
     private SIPRequest lastRequest;
 
     private int viaPort;
 
     private String viaHost;
 
     // Real ResponseInterface to pass messages to
     private transient ServerResponseInterface respondTo;
 
     private SIPDialog defaultDialog;
 
     private Hop nextHop;
 
     private boolean notifyOnRetransmit;
 
     private boolean timeoutIfStillInCallingState;
 
     private int callingStateTimeoutCount;
 
     public class TransactionTimer extends SIPStackTimerTask {
 
         public TransactionTimer() {
 
         }
 
         protected void runTask() {
             SIPClientTransaction clientTransaction;
             SIPTransactionStack sipStack;
             clientTransaction = SIPClientTransaction.this;
             sipStack = clientTransaction.sipStack;
 
             // If the transaction has terminated,
             if (clientTransaction.isTerminated()) {
 
                 if (sipStack.isLoggingEnabled()) {
                     sipStack.getStackLogger().logDebug(
                             "removing  = " + clientTransaction + " isReliable "
                                     + clientTransaction.isReliable());
                 }
 
                 sipStack.removeTransaction(clientTransaction);
 
                 try {
                     this.cancel();
 
                 } catch (IllegalStateException ex) {
                     if (!sipStack.isAlive())
                         return;
                 }
 
                 // Client transaction terminated. Kill connection if
                 // this is a TCP after the linger timer has expired.
                 // The linger timer is needed to allow any pending requests to
                 // return responses.
                 if ((!sipStack.cacheClientConnections) && clientTransaction.isReliable()) {
 
                     int newUseCount = --clientTransaction.getMessageChannel().;
                     if (newUseCount <= 0) {
                         // Let the connection linger for a while and then close
                         // it.
                         TimerTask myTimer = new LingerTimer();
                         sipStack.getTimer().schedule(myTimer,
                                 . * 1000);
                     }
 
                 } else {
                     // Cache the client connections so dont close the
                     // connection. This keeps the connection open permanently
                     // until the client disconnects.
                     if (sipStack.isLoggingEnabled() && clientTransaction.isReliable()) {
                        	int useCount = clientTransaction.getMessageChannel().;
                        	if (sipStack.isLoggingEnabled())
                        		sipStack.getStackLogger().logDebug("Client Use Count = " + useCount);
                     }
                 }
 
             } else {
                 // If this transaction has not
                 // terminated,
                 // Fire the transaction timer.
                 clientTransaction.fireTimer();
 
             }
 
         }
 
     }

    
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( SIPConstants.BRANCH_MAGIC_COOKIE +
         // Integer.toHexString( hashCode( ) ) );
         setBranch(Utils.getInstance().generateBranchId());
         this. = newChannelToUse.messageProcessor;
         this.setEncapsulatedChannel(newChannelToUse);
         this. = false;
         this. = false;
 
         // This semaphore guards the listener from being
         // re-entered for this transaction. That is
         // for a give tx, the listener is called at most
         // once with an outstanding request.
 
         if (.isLoggingEnabled()) {
             .getStackLogger().logDebug("Creating clientTransaction " + this);
             .getStackLogger().logStackTrace();
         }
         // this.startTransactionTimer();
         this. = new ConcurrentHashMap();
     }

    
Sets the real ResponseInterface this transaction encapsulates.

Parameters:
newRespondTo ResponseInterface to send messages to.
 
     public void setResponseInterface(ServerResponseInterface newRespondTo) {
         if (.isLoggingEnabled()) {
             .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
         ViaList viaHeaders = messageToTest.getViaHeaders();
         // Flags whether the select message is part of this transaction
         boolean transactionMatches;
         String messageBranch = ((ViaviaHeaders.getFirst()).getBranch();
         boolean rfc3261Compliant = getBranch() != null
                 && messageBranch != null
                 && getBranch().toLowerCase().startsWith(
                         .)
                 && messageBranch.toLowerCase().startsWith(
                         .);
 
         transactionMatches = false;
         if (. == this.getState()) {
             if (rfc3261Compliant) {
                 transactionMatches = getBranch().equalsIgnoreCase(
                         ((ViaviaHeaders.getFirst()).getBranch())
                         && getMethod().equals(messageToTest.getCSeq().getMethod());
             } else {
                 transactionMatches = getBranch().equals(messageToTest.getTransactionId());
             }
         } else if (!isTerminated()) {
             if (rfc3261Compliant) {
                 if (viaHeaders != null) {
                     // If the branch parameter is the
                     // same as this transaction and the method is the same,
                     if (getBranch().equalsIgnoreCase(((ViaviaHeaders.getFirst()).getBranch())) {
                         transactionMatches = getOriginalRequest().getCSeq().getMethod().equals(
                                 messageToTest.getCSeq().getMethod());
 
                     }
                 }
             } else {
                 // not RFC 3261 compliant.
                 if (getBranch() != null) {
                     transactionMatches = getBranch().equalsIgnoreCase(
                             messageToTest.getTransactionId());
                 } else {
                     transactionMatches = getOriginalRequest().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.getViaHeaders().getFirst();
             // 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 (. == getState()
                     || . == getState()) {
 
                 // 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(.);
                     }
                     // 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 (getState() == null) {
                     // 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(.);
                     } 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 (getState() == null)
             return;
 
         // Ignore 1xx
         if ((. == this.getState() || . == this
                 .getState())
                 && transactionResponse.getStatusCode() / 100 == 1) {
             return;
         }
 
         if (.isLoggingEnabled()) {
             .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.getState()) {
             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) {
                 // Send the response up to the TU.
                 if ( != null) {
                     .processResponse(transactionResponsethissipDialog);
                 } else {
                     this.semRelease();
                 }
                 if (!isReliable()) {
                     this.setState(.);
                     enableTimeoutTimer();
                 } else {
                     this.setState(.);
                 }
             }
         } else if (. == this.getState()) {
             if (statusCode / 100 == 1) {
                 if ( != null) {
                     .processResponse(transactionResponsethissipDialog);
                 } else {
                     this.semRelease();
                 }
             } else if (200 <= statusCode && statusCode <= 699) {
                 if ( != null) {
                     .processResponse(transactionResponsethissipDialog);
                 } else {
                     this.semRelease();
                 }
                 disableRetransmissionTimer();
                 disableTimeoutTimer();
                 if (!isReliable()) {
                     this.setState(.);
                     enableTimeoutTimer();
                 } else {
                     this.setState(.);
                 }
             }
         } else {
             if (.isLoggingEnabled()) {
                 .getStackLogger().logDebug(
                         " Not sending response to TU! " + getState());
             }
             this.semRelease();
         }
     }

    
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.getState()) {
             boolean ackAlreadySent = false;
             if (dialog != null && dialog.isAckSeen() && dialog.getLastAckSent() != null) {
                 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.getState()) {
             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 ( != null) {
                     .processResponse(transactionResponsethisdialog);
                 } else {
                     this.semRelease();
                 }
 
                 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(.);
                 }
             }
         } else if (. == this.getState()) {
             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(.);
                 }
 
                 // 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.getState()) {
             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.getState() != null)
             throw new SipException("Request already sent");
 
         if (.isLoggingEnabled()) {
             .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.getOriginalRequest().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.getState() == null) {
                     throw new SipException(
                             "State is null no provisional response yet -- cannot cancel RFC 3261 9.1");
                 } else if (!ct.getMethod().equals(.)) {
                     throw new SipException("Cannot cancel non-invite requests RFC 3261 9.1");
                 }
             } else
 
             if (this.getOriginalRequest().getMethod().equals(.)
                     || this.getOriginalRequest().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 (this.getMethod().equals(.)) {
                 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;
             this.sendMessage(sipRequest);
 
         } catch (IOException ex) {
             this.setState(.);
             throw new SipException("IO Error sending request"ex);
 
         }
 
     }

    
Called by the transaction stack when a retransmission timer fires.
 
     protected void fireRetransmissionTimer() {
 
         try {
 
             // Resend the last request sent
             if (this.getState() == null || !this.)
                 return;
 
             boolean inv = isInviteTransaction();
             TransactionState s = this.getState();
 
             // 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.getState() == .) {
                        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() {
        if (.isLoggingEnabled())
            .getStackLogger().logDebug("fireTimeoutTimer " + this);
        SIPDialog dialog = (SIPDialogthis.getDialog();
        if (. == this.getState()
                || . == this.getState()
                || . == this.getState()) {
            // Timeout occured. If this is asociated with a transaction
            // creation then kill the dialog.
            if (dialog != null
                    && (dialog.getState() == null || dialog.getState() == .)) {
                if (((SIPTransactionStackgetSIPStack()).isDialogCreated(this
                        .getOriginalRequest().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 (getOriginalRequest().getMethod().equalsIgnoreCase(.)
                        && dialog.isTerminatedOnBye()) {
                    // Terminate the associated dialog on BYE Timeout.
                    dialog.delete();
                }
            }
        }
        if (. != this.getState()) {
            // Got a timeout error on a cancel.
            if (this.getOriginalRequest().getMethod().equalsIgnoreCase(.)) {
                SIPClientTransaction inviteTx = (SIPClientTransactionthis.getOriginalRequest()
                        .getInviteTransaction();
                if (inviteTx != null
                        && ((inviteTx.getState() == . || inviteTx
                                .getState() == .))
                        && 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 (!((SipURI) ((RouterouteList.getFirst()).getAddress().getURI()).hasLrParam()) {
            // Contact may not yet be there (bug reported by Andreas B).
            Route route = null;
            if (contact != null) {
                route = new Route();
                route.setAddress((AddressImpl) ((AddressImpl) (contact.getAddress())).clone());
            }
            Route firstRoute = (RouterouteList.getFirst();
            routeList.removeFirst();
            javax.sip.address.URI uri = firstRoute.getAddress().getURI();
            ackRequest.setRequestURI(uri);
            if (route != null)
                routeList.add(route);
            ackRequest.addHeader(routeList);
        } else {
            if (contact != null) {
                javax.sip.address.URI uri = (javax.sip.address.URIcontact.getAddress().getURI()
                        .clone();
                ackRequest.setRequestURI(uri);
                ackRequest.addHeader(routeList);
            }
        }
        return ackRequest;
    }
    /*
     * Creates an ACK for an error response, according to RFC3261 section 17.1.1.3
     * 
     * Note that this is different from an ACK for 2xx
     */
    private final Request createErrorAck() throws SipExceptionParseException {
        SIPRequest originalRequest = this.getOriginalRequest();
        if (originalRequest == null)
            throw new SipException("bad state " + getState());
        if (!getMethod().equals(.)) {
            throw new SipException("Can only ACK an INVITE!");
        } 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!");
        }
        return originalRequest.createErrorAck((To.getTo());
    }

    
Set the port of the recipient.
    protected void setViaPort(int port) {
        this. = port;
    }

    
Set the port of the recipient.
    protected void setViaHost(String host) {
        this. = host;
    }

    
Get the port of the recipient.
    public int getViaPort() {
        return this.;
    }

    
Get the host of the recipient.
    public String getViaHost() {
        return this.;
    }

    
get the via header for an outgoing request.
    public Via getOutgoingViaHeader() {
        return this.getMessageProcessor().getViaHeader();
    }

    
This is called by the stack after a non-invite client transaction goes to completed state.
    public void clearState() {
        // reduce the state to minimum
        // This assumes that the application will not need
        // to access the request once the transaction is
        // completed.
        // TODO -- revisit this - results in a null pointer
        // occuring occasionally.
        // this.lastRequest = null;
        // this.originalRequest = null;
        // this.lastResponse = null;
    }

    
Sets a timeout after which the connection is closed (provided the server does not use the connection for outgoing requests in this time period) and calls the superclass to set state.
    public void