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;
  
  /*
   * Bug fixes / enhancements:Emil Ivov, Antonis Karydas, Daniel J. Martinez Manzano, Daniel, Hagai
   * Sela, Vazques-Illa, Bill Roome, Thomas Froment and Pierre De Rop, Christophe Anzille and Jeroen
   * van Bemmel, Frank Reif.
   * Carolyn Beeton ( Avaya ).
   *
   */

Represents a server transaction. Implements the following state machines.



                                                                      |INVITE
                                                                      |pass INV to TU
                                                   INVITE             V send 100 if TU won't in 200ms
                                                   send response+-----------+
                                                       +--------|           |--------+101-199 from TU
                                                       |        | Proceeding|        |send response
                                                       +------->|           |<-------+
                                                                |           |          Transport Err.
                                                                |           |          Inform TU
                                                                |           |--------------->+
                                                                +-----------+                |
                                                   300-699 from TU |     |2xx from TU        |
                                                   send response   |     |send response      |
                                                                   |     +------------------>+
                                                                   |                         |
                                                   INVITE          V          Timer G fires  |
                                                   send response+-----------+ send response  |
                                                       +--------|           |--------+       |
                                                       |        | Completed |        |       |
                                                       +------->|           |<-------+       |
                                                                +-----------+                |
                                                                   |     |                   |
                                                               ACK |     |                   |
                                                               -   |     +------------------>+
                                                                   |        Timer H fires    |
                                                                   V        or Transport Err.|
                                                                +-----------+  Inform TU     |
                                                                |           |                |
                                                                | Confirmed |                |
                                                                |           |                |
                                                                +-----------+                |
                                                                      |                      |
                                                                      |Timer I fires         |
                                                                      |-                     |
                                                                      |                      |
                                                                      V                      |
                                                                +-----------+                |
                                                                |           |                |
                                                                | Terminated|<---------------+
                                                                |           |
                                                                +-----------+

                                                     Figure 7: INVITE server transaction
                                                         Request received
                                                                         |pass to TU

                                                                         V
                                                                   +-----------+
                                                                   |           |
                                                                   | Trying    |-------------+
                                                                   |           |             |
                                                                   +-----------+             |200-699 from TU
                                                                         |                   |send response
                                                                         |1xx from TU        |
                                                                         |send response      |
                                                                         |                   |
                                                      Request            V      1xx from TU  |
                                                      send response+-----------+send response|
                                                          +--------|           |--------+    |
                                                          |        | Proceeding|        |    |
                                                          +------->|           |<-------+    |
                                                   +<--------------|           |             |
                                                   |Trnsprt Err    +-----------+             |
                                                   |Inform TU            |                   |
                                                   |                     |                   |
                                                   |                     |200-699 from TU    |
                                                   |                     |send response      |
                                                   |  Request            V                   |
                                                   |  send response+-----------+             |
                                                   |      +--------|           |             |
                                                   |      |        | Completed |<------------+
                                                   |      +------->|           |
                                                   +<--------------|           |
                                                   |Trnsprt Err    +-----------+
                                                   |Inform TU            |
                                                   |                     |Timer J fires
                                                   |                     |-
                                                   |                     |
                                                   |                     V
                                                   |               +-----------+
                                                   |               |           |
                                                   +-------------->| Terminated|
                                                                   |           |
                                                                   +-----------+





 

Author(s):
M. Ranganathan
Version:
1.2 $Revision: 1.150 $ $Date: 2010-12-02 22:04:15 $
 
 public class SIPServerTransaction extends SIPTransaction implements ServerRequestInterface,
     private static StackLogger logger = CommonLogger.getLogger(SIPServerTransaction.class);
     public static final String CONTENT_TYPE_APPLICATION = "application";
     public static final String CONTENT_SUBTYPE_SDP = "sdp";
     // force the listener to see transaction
 
     private int rseqNumber = -1;
 
     // private LinkedList pendingRequests;
 
     // Real RequestInterface to pass messages to
     private transient ServerRequestInterface requestOf;
 
     private SIPDialog dialog;
     // jeand needed because we nullify the dialog ref early and keep only the dialogId to save on mem and help GC
     protected String dialogId;
 
     // the unacknowledged SIPResponse
 
 //    private SIPResponse pendingReliableResponse;
     // wondering if the pendingReliableResponseAsBytes could be put into the lastResponseAsBytes
     private byte[] pendingReliableResponseAsBytes;
     private long pendingReliableCSeqNumber;
     private long pendingReliableRSeqNumber;
 
     // The pending reliable Response Timer
 
     private boolean retransmissionAlertEnabled;
 
     
 
     protected boolean isAckSeen;
 
 
 
     // Experimental.
     private static boolean interlockProvisionalResponses = true;
 
     private Semaphore provisionalResponseSem = new Semaphore(1);
     
     private Semaphore terminationSemaphore = new Semaphore(0);
 
     // jeand we nullify the last response fast to save on mem and help GC, but we keep only the information needed
     private byte[] lastResponseAsBytes;
     private String lastResponseHost;
     private int lastResponsePort;
     private String lastResponseTransport;
 
     private int lastResponseStatusCode;
 
     private HostPort originalRequestSentBy;
     private String originalRequestFromTag;

    
This timer task is used for alerting the application to send retransmission alerts.
 
 
         String dialogId;
 
         int ticks;
 
         int ticksLeft;
 
         public RetransmissionAlertTimerTask(String dialogId) {
 
             this. = .;
             this. = this.;
         }
 
         public void runTask() {
             SIPServerTransaction serverTransaction = SIPServerTransaction.this;
             --;
             if ( == -1) {
                 serverTransaction.fireRetransmissionTimer();
                 this. = 2 * ;
             }
 
         }
 
     }
 
     class ProvisionalResponseTask extends SIPStackTimerTask {
 
         int ticks;
 
         int ticksLeft;
 
         public ProvisionalResponseTask() {
             this. = .;
             this. = this.;
         }
 
         public void runTask() {
             SIPServerTransaction serverTransaction = SIPServerTransaction.this;
             /*
              * The reliable provisional response is passed to the transaction layer periodically
              * with an interval that starts at T1 seconds and doubles for each retransmission (T1
              * is defined in Section 17 of RFC 3261). Once passed to the server transaction, it is
              * added to an internal list of unacknowledged reliable provisional responses. The
              * transaction layer will forward each retransmission passed from the UAS core.
              *
              * This differs from retransmissions of 2xx responses, whose intervals cap at T2
              * seconds. This is because retransmissions of ACK are triggered on receipt of a 2xx,
              * but retransmissions of PRACK take place independently of reception of 1xx.
              */
             // If the transaction has terminated,
             if (serverTransaction.isTerminated()) {
 
                 .getTimer().cancel(this);
 
             } else {
                 --;
                 if ( == -1) {
                     serverTransaction.fireReliableResponseRetransmissionTimer();
                     this. = 2 * ;
                     this. = this.;
                     // timer H MUST be set to fire in 64*T1 seconds for all transports. Timer H
                     // determines when the server
                     // transaction abandons retransmitting the response
                     if (this. >= .) {
                         .getTimer().cancel(this);
                         setState(.);
                         fireTimeoutTimer();
                     }
                 }
 
             }
 
         }
 
     }

    
This timer task will terminate the transaction if the listener does not respond in a pre-determined time period. This helps prevent buggy listeners (who fail to respond) from causing memory leaks. This allows a container to protect itself from buggy code ( that fails to respond to a server transaction).
 
     class ListenerExecutionMaxTimer extends SIPStackTimerTask {
 
         ListenerExecutionMaxTimer() {
         }
 
         public void runTask() {
             try {
                	 = null;
             	if (.isLoggingEnabled(.))
                     .logDebug("Fired ListenerExecutionMaxTimer for stx " + .getTransactionId() + " state " + .getState());
             	if (.getState().getValue() < 0 
             			// may have been forcefully TERMINATED through terminate() method but if the tx timer never got scheduled
                 		// it wouldn't be reaped
                 		|| .getInternalState() >= 5) {
                     .terminate();
                     SIPTransactionStack sipStack = .getSIPStack();
                     sipStack.removePendingTransaction();
                     sipStack.removeTransaction();
 
                 }
             } catch (Exception ex) {
                 .logError("unexpected exception"ex);
             }
         }
     }

    
This timer task is for INVITE server transactions. It will send a trying in 200 ms. if the TU does not do so.
 
     class SendTrying extends SIPStackTimerTask {
 
         protected SendTrying() {
             if (.isLoggingEnabled(.))
                 .logDebug("scheduled timer for " + SIPServerTransaction.this);
 
         }
 
         public void runTask() {
             SIPServerTransaction serverTransaction = SIPServerTransaction.this;
 
             int realState = serverTransaction.getRealState();
 
             if (realState < 0 || . == realState) {
                 if (.isLoggingEnabled(.))
                     .logDebug(" sending Trying current state = "
                             + serverTransaction.getRealState());
                 try {
                     serverTransaction.sendMessage(serverTransaction.getOriginalRequest()
                             .createResponse(100, "Trying"));
                     if (.isLoggingEnabled(.))
                         .logDebug(" trying sent "
                                 + serverTransaction.getRealState());
                 } catch (IOException ex) {
                     if (.isLoggingEnabled())
                         .logError("IO error sending  TRYING");
                 }
             }
 
         }
     }
 
     class TransactionTimer extends SIPStackTimerTask {
 
         public TransactionTimer() {
             if (.isLoggingEnabled(.)) {
                 .logDebug("TransactionTimer() : " + getTransactionId());
             }
 
         }
 
         public void runTask() {
             // If the transaction has terminated,
             if (isTerminated()) {
                 // Keep the transaction hanging around in the transaction table
                 // to catch the incoming ACK -- this is needed for tcp only.
                 // Note that the transaction record is actually removed in
                 // the connection linger timer.
                 try {
                        .getTimer().cancel(this);
                        if( != null) {
                     	   .getTimer().cancel();
                        }
                 } catch (IllegalStateException ex) {
                     if (!.isAlive())
                         return;
                 }
 
 
                 // Oneshot timer that garbage collects the SeverTransaction
                 // after a scheduled amount of time. The linger timer allows
                 // the client side of the tx to use the same connection to
                 // send an ACK and prevents a race condition for creation
                 // of new server tx
                 SIPStackTimerTask myTimer = new LingerTimer();
 
                 .getTimer().schedule(myTimer,
                     . * 1000);
             } else {
                 // Add to the fire list -- needs to be moved
                 // outside the synchronized block to prevent
                 // deadlock.
                 fireTimer();
             }
             if( != null) {
                 .cleanUp();
             }
         }
 
     }

    
Send a response.

Parameters:
transactionResponse -- the response to send
 
 
     protected void sendResponse(SIPResponse transactionResponsethrows IOException {
         if ( .isLoggingEnabled(.)) {
             .logDebug("sipServerTransaction::sendResponse " + transactionResponse.getFirstLine());
         }
         try {
             // RFC18.2.2. Sending Responses
             // The server transport uses the value of the top Via header field
             // in
             // order
             // to determine where to send a response.
             // It MUST follow the following process:
             // If the "sent-protocol" is a reliable transport
             // protocol such as TCP or SCTP,
             // or TLS over those, the response MUST be
             // sent using the existing connection
             // to the source of the original request
             // that created the transaction, if that connection is still open.
             if (isReliable()) {
 
                 getMessageChannel().sendMessage(transactionResponse);
 
 
             } else {
                 Via via = transactionResponse.getTopmostVia();
                 String transport = via.getTransport();
                 if (transport == null)
                     throw new IOException("missing transport!");
                 // @@@ hagai Symmetric NAT support
                 int port = via.getRPort();
                 if (port == -1)
                     port = via.getPort();
                 if (port == -1) {
                     if (transport.equalsIgnoreCase("TLS"))
                         port = 5061;
                     else
                         port = 5060;
                 }
 
                 // Otherwise, if the Via header field value contains a
                 // "maddr" parameter, the response MUST be forwarded to
                 // the address listed there, using the port indicated in
                 // "sent-by",
                 // or port 5060 if none is present. If the address is a
                 // multicast
                 // address, the response SHOULD be sent using
                 // the TTL indicated in the "ttl" parameter, or with a
                 // TTL of 1 if that parameter is not present.
                 String host = null;
                 if (via.getMAddr() != null) {
                     host = via.getMAddr();
                 } else {
                     // Otherwise (for unreliable unicast transports),
                     // if the top Via has a "received" parameter, the response
                     // MUST
                     // be sent to the
                     // address in the "received" parameter, using the port
                     // indicated
                     // in the
                     // "sent-by" value, or using port 5060 if none is specified
                     // explicitly.
                     host = via.getParameter(.);
                     if (host == null) {
                         // Otherwise, if it is not receiver-tagged, the response
                         // MUST be
                         // sent to the address indicated by the "sent-by" value,
                         // using the procedures in Section 5
                         // RFC 3263 PROCEDURE TO BE DONE HERE
                         host = via.getHost();
                     }
                 }
 
                 Hop hop = ..resolveAddress(new HopImpl(hostport,
                         transport));
 
                 MessageChannel messageChannel = ((SIPTransactionStackgetSIPStack())
                         .createRawMessageChannel(this.getSipProvider().getListeningPoint(
                                 hop.getTransport()).getIPAddress(), this.getPort(), hop);
                 if (messageChannel != null) {
                     messageChannel.sendMessage(transactionResponse);
                      = host;
                      = port;
                      = transport;
                 } else {
                     throw new IOException("Could not create a message channel for " + hop + " with source IP:Port "+
                             this.getSipProvider().getListeningPoint(
                                     hop.getTransport()).getIPAddress() + ":" + this.getPort());
                 }
 
             }
              = transactionResponse.encodeAsBytes(this.getTransport());
              = null;
         } finally {
             this.startTransactionTimer();
         }
     }

    
Creates a new server transaction.

Parameters:
sipStack Transaction stack this transaction belongs to.
newChannelToUse Channel to encapsulate.
 
     protected SIPServerTransaction(SIPTransactionStack sipStackMessageChannel newChannelToUse) {
 
         super(sipStacknewChannelToUse);
 
         if (sipStack.maxListenerResponseTime != -1) {
             sipStack.getTimer().schedule(new ListenerExecutionMaxTimer(),
                     sipStack.maxListenerResponseTime * 1000);
         }
         // Only one outstanding request for a given server tx.
 
         if (.isLoggingEnabled(.)) {
             .logDebug("Creating Server Transaction" + this.getBranchId());
             .logStackTrace();
         }
 
     }

    
Sets the real RequestInterface this transaction encapsulates.

Parameters:
newRequestOf RequestInterface to send messages to.
 
     public void setRequestInterface(ServerRequestInterface newRequestOf) {
 
          = newRequestOf;
 
     }

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



    
Determines 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;
 
         // Flags whether the select message is part of this transaction
         boolean transactionMatches = false;
         final String method = messageToTest.getCSeq().getMethod();
         SIPRequest origRequest = getOriginalRequest();
         // Invite Server transactions linger in the terminated state in the
         // transaction
         // table and are matched to compensate for
         // http://bugs.sipit.net/show_bug.cgi?id=769
         if (isInviteTransaction() || !isTerminated()) {
 
             // Get the topmost Via header and its branch parameter
             final Via topViaHeader = messageToTest.getTopmostVia();
             if (topViaHeader != null) {
 
 //                topViaHeader = (Via) viaHeaders.getFirst();
                  // Branch code in the topmost Via header
                 String messageBranch = topViaHeader.getBranch();
                 if (messageBranch != null) {
 
                     // If the branch parameter exists but
                     // does not start with the magic cookie,
                     if (!messageBranch.toLowerCase().startsWith(
                             .)) {
 
                         // Flags this as old
                         // (RFC2543-compatible) client
                         // version
                         messageBranch = null;
 
                     }
 
                 }
 
                 // If a new branch parameter exists,
                 if (messageBranch != null && this.getBranch() != null) {
                     if (method.equals(.)) {
                         // Cancel is handled as a special case because it
                         // shares the same same branch id of the invite
                         // that it is trying to cancel.
                         transactionMatches = this.getMethod().equals(.)
                                 && getBranch().equalsIgnoreCase(messageBranch)
                                 && topViaHeader.getSentBy().equals(
                                          origRequest.getTopmostVia()
                                                 .getSentBy());
 
                     } else {
                         // Matching server side transaction with only the
                         // branch parameter.
                         if(origRequest != null) {
                             transactionMatches = getBranch().equalsIgnoreCase(messageBranch)
                                 && topViaHeader.getSentBy().equals(
                                           origRequest.getTopmostVia()
                                                 .getSentBy());
                         } else {
                             transactionMatches = getBranch().equalsIgnoreCase(messageBranch)
                                 && topViaHeader.getSentBy().equals();
                         }
 
                     }
 
                 } else {
                     // force the reparsing only on non RFC 3261 messages
                     origRequest = (SIPRequestgetRequest();
 
                     // This is an RFC2543-compliant message; this code is here
                     // for backwards compatibility.
                     // It is a weak check.
                     // If RequestURI, To tag, From tag, CallID, CSeq number, and
                     // top Via headers are the same, the
                     // SIPMessage matches this transaction. An exception is for
                     // a CANCEL request, which is not deemed
                     // to be part of an otherwise-matching INVITE transaction.
                     String originalFromTag = origRequest.getFromTag();
 
                     String thisFromTag = messageToTest.getFrom().getTag();
 
                     boolean skipFrom = (originalFromTag == null || thisFromTag == null);
 
                     String originalToTag = origRequest.getToTag();
 
                     String thisToTag = messageToTest.getTo().getTag();
 
                     boolean skipTo = (originalToTag == null || thisToTag == null);
                     boolean isResponse = (messageToTest instanceof SIPResponse);
                     // Issue #96: special case handling for a CANCEL request -
                     // the CSeq method of the original request must
                     // be CANCEL for it to have a chance at matching.
                     if (messageToTest.getCSeq().getMethod().equalsIgnoreCase(.)
                             && !origRequest.getCSeq().getMethod().equalsIgnoreCase(
                                     .)) {
                         transactionMatches = false;
                     } else if ((isResponse || origRequest.getRequestURI().equals(
                             ((SIPRequestmessageToTest).getRequestURI()))
                             && (skipFrom || originalFromTag != null && originalFromTag.equalsIgnoreCase(thisFromTag))
                             && (skipTo || originalToTag != null && originalToTag.equalsIgnoreCase(thisToTag))
                             && origRequest.getCallId().getCallId().equalsIgnoreCase(
                                     messageToTest.getCallId().getCallId())
                             && origRequest.getCSeq().getSeqNumber() == messageToTest
                                     .getCSeq().getSeqNumber()
                             && ((!messageToTest.getCSeq().getMethod().equals(.)) ||
                                     getMethod().equals(messageToTest.getCSeq().getMethod()))
                             && topViaHeader.equals(origRequest.getTopmostVia())) {
 
                         transactionMatches = true;
                     }
 
                 }
 
             }
 
         }
         return transactionMatches;
 
     }

    
Send out a trying response (only happens when the transaction is mapped). Otherwise the transaction is not known to the stack.
 
     protected void map() {
         // note that TRYING is a pseudo-state for invite transactions
 
         int realState = getRealState();
 
         if (realState < 0 || realState == .) {
             // Also sent by intermediate proxies.
             // null check added as the stack may be stopped. TRYING is not sent by reliable transports.
             if (isInviteTransaction() && !this. && .getTimer() != null ) {
                 this. = true;
                 // Schedule a timer to fire in 200 ms if the
                 // TU did not send a trying in that time.
                 .getTimer().schedule(new SendTrying(), 200);
 
             } else {
                  = true;
             }
         }
 
         // Pull it out of the pending transactions list.
         .removePendingTransaction(this);
     }

    
Return true if the transaction is known to stack.
 
     public boolean isTransactionMapped() {
         return this.;
     }

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

Parameters:
transactionRequest Request to process.
sourceChannel Channel that received this message.
 
     public void processRequest(SIPRequest transactionRequestMessageChannel sourceChannel) {
         boolean toTu = false;
 
         // Can only process a single request directed to the
         // transaction at a time. For a given server transaction
         // the listener sees only one event at a time.
 
         if (.isLoggingEnabled(.)) {
             .logDebug("processRequest: " + transactionRequest.getFirstLine());
             .logDebug("tx state = " + this.getRealState());
         }
 
         try {
 
             // If this is the first request for this transaction,
             if (getRealState() < 0) {
                 // Save this request as the one this
                 // transaction is handling
                 setOriginalRequest(transactionRequest);
                 this.setState(.);
                 toTu = true;
                 this.setPassToListener();
 
                 // Rsends the TRYING on retransmission of the request.
                 if (isInviteTransaction() && this.) {
                     // JvB: also
                     // proxies need
                     // to do this
 
                     // Has side-effect of setting
                     // state to "Proceeding"
                     sendMessage(transactionRequest.createResponse(100, "Trying"));
 
                 }
                 // If an invite transaction is ACK'ed while in
                 // the completed state,
             } else if (isInviteTransaction() && . == getRealState()
                     && transactionRequest.getMethod().equals(.)) {
 
                 // @jvB bug fix
                 this.setState(.);
                 disableRetransmissionTimer();
                 if (!isReliable()) {
                     enableTimeoutTimer();
 
                 } else {
 
                     this.setState(.);
 
                 }
 
 
                 // JvB: For the purpose of testing a TI, added a property to
                 // pass it anyway
                 if (.isNon2XXAckPassedToListener()) {
                     // This is useful for test applications that want to see
                     // all messages.
                     .processRequest(transactionRequestthis);
                 } else {
                     // According to RFC3261 Application should not Ack in
                     // CONFIRMED state
                     if (.isLoggingEnabled(.)) {
                         .logDebug("ACK received for server Tx "
                                 + this.getTransactionId() + " not delivering to application!");
 
                     }
 
                     this.semRelease();
                 }
                 return;
 
                 // If we receive a retransmission of the original
                 // request,
             } else if (transactionRequest.getMethod().equals(getMethod())) {
 
                 if (. == getRealState()
                         || . == getRealState()) {
                     this.semRelease();
                     // Resend the last response to
                     // the client
                     // Send the message to the client
                     resendLastResponseAsBytes();
                 } else if (transactionRequest.getMethod().equals(.)) {
                     // This is passed up to the TU to suppress
                     // retransmission of OK
                     if ( != null)
                         .processRequest(transactionRequestthis);
                     else
                         this.semRelease();
                 }
                 if (.isLoggingEnabled(.))
                     .logDebug("completed processing retransmitted request : "
                         + transactionRequest.getFirstLine() + this + " txState = "
                         + this.getState() + " lastResponse = " + this.);
                 return;
 
             }
 
             // Pass message to the TU
             if (. != getRealState()
                     && . != getRealState() &&  != null) {
                 if (getMethod().equals(transactionRequest.getMethod())) {
                     // Only send original request to TU once!
                     if (toTu) {
                         .processRequest(transactionRequestthis);
                     } else
                         this.semRelease();
                 } else {
                     if ( != null)
                         .processRequest(transactionRequestthis);
                     else
                         this.semRelease();
                 }
             } else {
                 // This seems like a common bug so I am allowing it through!
                 if (SIPTransactionStack.isDialogCreated(getMethod())
                         && getRealState() == .
                         && transactionRequest.getMethod().equals(.)
                         &&  != null) {
                     SIPDialog thisDialog = (SIPDialoggetDialog();
 
                     if (thisDialog == null || !thisDialog.ackProcessed) {
                         // Filter out duplicate acks
                         if (thisDialog != null) {
                             thisDialog.ackReceived(transactionRequest.getCSeq().getSeqNumber());
                             thisDialog.ackProcessed = true;
                         }
                         .processRequest(transactionRequestthis);
                     } else {
                         this.semRelease();
                     }
 
                 } else if (transactionRequest.getMethod().equals(.)) {
                     if (.isLoggingEnabled(.))
                         .logDebug("Too late to cancel Transaction");
                     this.semRelease();
                     // send OK and just ignore the CANCEL.
                     try {
                         this.sendMessage(transactionRequest.createResponse(.));
                     } catch (IOException ex) {
                         // Transaction is already terminated
                         // just ignore the IOException.
                     }
                 }
                 if (.isLoggingEnabled(.))
                     .logDebug("Dropping request " + getRealState());
             }
 
         } catch (IOException e) {
             if (.isLoggingEnabled())
                 .logError("IOException " ,e);
             this.semRelease();
             this.raiseIOExceptionEvent();
         }
 
     }

    
Send a response message through this transaction and onto the client. The response drives the state machine.

Parameters:
messageToSend Response to process and send.
 
     public void sendMessage(SIPMessage messageToSendthrows IOException {
         if ( .isLoggingEnabled(.)) {
             .logDebug("sipServerTransaction::sendMessage " + messageToSend.getFirstLine());
         }
         // Message typecast as a response
         final SIPResponse  transactionResponse = (SIPResponsemessageToSend;
         // Status code of the response being sent to the client
         final int statusCode = transactionResponse.getStatusCode();
         try {
 
             try {
                 // Provided we have set the banch id for this we set the BID for
                 // the
                 // outgoing via.
                 if ( != null)
                     transactionResponse.getTopmostVia().setBranch(this.getBranch());
                 else
                     transactionResponse.getTopmostVia().removeParameter(.);
 
                 // Make the topmost via headers match identically for the
                 // transaction rsponse.
                 if (!)
                     transactionResponse.getTopmostVia().removePort();
             } catch (ParseException ex) {
                .logError("UnexpectedException",ex);
                throw new IOException("Unexpected exception");
             }
 
             // Method of the response does not match the request used to
             // create the transaction - transaction state does not change.
             if (!transactionResponse.getCSeq().getMethod().equals(
                     getMethod())) {
                 sendResponse(transactionResponse);
                 return;
             }
 
             if(!checkStateTimers(statusCode)) {
                 if (.isLoggingEnabled(.)) {
                     .logDebug("checkStateTimers returned false -- not sending message");
                 }
                 return;
             }
 
             try {
                 // Send the message to the client.
                 // Record the last message sent out.
                 if (.isLoggingEnabled(.)) {
                     .logDebug(
                             "sendMessage : tx = " + this + " getState = " + this.getState());
                 }
                  = transactionResponse;
                  = transactionResponse.getStatusCode();
 
                 this.sendResponse(transactionResponse);
 
             } catch (IOException e) {
 
                 this.setState(.);
                 this. = 0;
                 throw e;
 
             }
         } finally {
             this.startTransactionTimer();
         }
 
     }
 
 
     private boolean checkStateTimers(int statusCode) {
         // If the TU sends a provisional response while in the
         // trying state,
 
         if (getRealState() == .) {
             if (statusCode / 100 == 1) {
                 this.setState(.);
             } else if (200 <= statusCode && statusCode <= 699) {
                 // INVITE ST has TRYING as a Pseudo state
                 // (See issue 76). We are using the TRYING
                // pseudo state invite Transactions
                // to signal if the application
                // has sent trying or not and hence this
                // check is necessary.
                if (!isInviteTransaction()) {
                    if (!isReliable() && getInternalState() != .) {
                        // Linger in the completed state to catch
                        // retransmissions if the transport is not
                        // reliable.
                        this.setState(.);
                        // Note that Timer J is only set for Unreliable
                        // transports -- see Issue 75.
                        /*
                         * From RFC 3261 Section 17.2.2 (non-invite server transaction)
                         *
                         * When the server transaction enters the "Completed" state, it MUST
                         * set Timer J to fire in 64*T1 seconds for unreliable transports, and
                         * zero seconds for reliable transports. While in the "Completed"
                         * state, the server transaction MUST pass the final response to the
                         * transport layer for retransmission whenever a retransmission of the
                         * request is received. Any other final responses passed by the TU to
                         * the server transaction MUST be discarded while in the "Completed"
                         * state. The server transaction remains in this state until Timer J
                         * fires, at which point it MUST transition to the "Terminated" state.
                         */
                        startTransactionTimerJ();
                        cleanUpOnTimer();
                    } else {
                        cleanUpOnTimer();
                        this.setState(.);
                        startTransactionTimerJ(0);
                    }
                } else {
                    // This is the case for INVITE server transactions.
                    // essentially, it duplicates the code in the
                    // PROCEEDING case below. There is no TRYING state for INVITE
                    // transactions in the RFC. We are using it to signal whether the
                    // application has sent a provisional response or not. Hence
                    // this is treated the same as as Proceeding.
                    if (statusCode / 100 == 2) {
                        // Status code is 2xx means that the
                        // transaction transitions to TERMINATED
                        // for both Reliable as well as unreliable
                        // transports. Note that the dialog layer
                        // takes care of retransmitting 2xx final
                        // responses.
                        /*
                         * RFC 3261 Section 13.3.1.4 Note, however, that the INVITE server
                         * transaction will be destroyed as soon as it receives this final
                         * response and passes it to the transport. Therefore, it is necessary
                         * to periodically pass the response directly to the transport until
                         * the ACK arrives. The 2xx response is passed to the transport with
                         * an interval that starts at T1 seconds and doubles for each
                         * retransmission until it reaches T2 seconds (T1 and T2 are defined
                         * in Section 17). Response retransmissions cease when an ACK request
                         * for the response is received. This is independent of whatever
                         * transport protocols are used to send the response.
                         */
                        this.disableRetransmissionTimer();
                        this.disableTimeoutTimer();
                        this. = ;
                        cleanUpOnTimer();
                        this.setState(.);
                        if (this.getDialog() != null)
                            ((SIPDialogthis.getDialog()).setRetransmissionTicks();
                    } else {
                        // This an error final response.
                        this.setState(.);
                        if (!isReliable()) {
                            /*
                             * RFC 3261
                             *
                             * While in the "Proceeding" state, if the TU passes a response
                             * with status code from 300 to 699 to the server transaction, the
                             * response MUST be passed to the transport layer for
                             * transmission, and the state machine MUST enter the "Completed"
                             * state. For unreliable transports, timer G is set to fire in T1
                             * seconds, and is not set to fire for reliable transports.
                             */
                            enableRetransmissionTimer();
                        }
                        cleanUpOnTimer();
                        enableTimeoutTimer();
                    }
                }
            }
            // If the transaction is in the proceeding state,
        } else if (getRealState() == .) {
            if (isInviteTransaction()) {
                // If the response is a failure message,
                if (statusCode / 100 == 2) {
                    // Set up to catch returning ACKs
                    // The transaction lingers in the
                    // terminated state for some time
                    // to catch retransmitted INVITEs
                    this.disableRetransmissionTimer();
                    this.disableTimeoutTimer();
                    this. = ;
                    cleanUpOnTimer();
                    this.setState(.);
                    if (this.getDialog() != null)
                        ((SIPDialogthis.getDialog()).setRetransmissionTicks();
                } else if (300 <= statusCode && statusCode <= 699) {
                    // Set up to catch returning ACKs
                    this.setState(.);
                    if (!isReliable()) {
                        /*
                         * While in the "Proceeding" state, if the TU passes a response with
                         * status code from 300 to 699 to the server transaction, the response
                         * MUST be passed to the transport layer for transmission, and the
                         * state machine MUST enter the "Completed" state. For unreliable
                         * transports, timer G is set to fire in T1 seconds, and is not set to
                         * fire for reliable transports.
                         */
                        enableRetransmissionTimer();
                    }
                    cleanUpOnTimer();
                    enableTimeoutTimer();
                }
                // If the transaction is not an invite transaction
                // and this is a final response,
            } else if (200 <= statusCode && statusCode <= 699) {
                // This is for Non-invite server transactions.
                // Set up to retransmit this response,
                // or terminate the transaction
                this.setState(.);
                if (!isReliable()) {
                    disableRetransmissionTimer();
//                    enableTimeoutTimer(TIMER_J);
                    startTransactionTimerJ();
                } else {
                    this.setState(.);
                    startTransactionTimerJ(0);
                }
                cleanUpOnTimer();
            }
            // If the transaction has already completed,
        } else if (. == this.getRealState()) {
            return false;
        }
        return true;
    }
    public String getViaHost() {
        return super.getViaHost();
    }
    public int getViaPort() {
        return super.getViaPort();
    }

    
Called by the transaction stack when a retransmission timer fires. This retransmits the last response when the retransmission filter is enabled.
    protected void fireRetransmissionTimer() {
        try {
            if (.isLoggingEnabled(.)) {
                .logDebug("fireRetransmissionTimer() -- " + this + " state " + getState());
            }
            // Resend the last response sent by this transaction
            if (isInviteTransaction() && ( != null ||  != null)) {
                // null can happen if this is terminating when the timer fires.
                if (!this. || .isTransactionPendingAck(this) ) {
                    // Retransmit last response until ack.
                    if ( / 100 >= 2 && !this.) {
                        resendLastResponseAsBytes();
                    }
                } else {
                    // alert the application to retransmit the last response
                    SipProviderImpl sipProvider = (SipProviderImplthis.getSipProvider();
                    TimeoutEvent txTimeout = new TimeoutEvent(sipProviderthis,
                            .);
                    sipProvider.handleEvent(txTimeoutthis);
                }
            }
        } catch (IOException e) {
            if (.isLoggingEnabled())
                .logException(e);
        }
    }
    // jeand we nullify the last response very fast to save on mem and help GC but we keep it as byte array
    // so this method is used to resend the last response either as a response or byte array depending on if it has been nullified
    public void resendLastResponseAsBytes() throws IOException {
        if( != null) {
            if (.isLoggingEnabled(.