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.125.2.2 $ $Date: 2010/11/23 19:23:13 $
 
 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();
 
             }
 
         }
 
     }
     
     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( 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.
 
             .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) {
             .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;
         }
 
             .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!= 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.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");
 
             .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;
             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  &&  getMethod().equals(.) &&
                    expiresTime != -1 &&  == null ) {
                this. = new ExpiresTimerTask();
                .getTimer().schedule(expiresTime * 1000);
                
            }
            this.sendMessage(sipRequest);
        } catch (IOException ex) {
            this.setState(.);
            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.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() {
            .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);