FileDocCategorySizeDatePackage
ClientTransaction.javaAPI DocphoneME MR2 API (J2ME)34930Wed May 02 18:00:42 BST 2007gov.nist.siplite.stack

ClientTransaction

public class ClientTransaction extends Transaction implements SIPServerResponseInterface
Represents a client transaction.
version
JAIN-SIP-1.1 This code is in the public domain.

Implements the following state machines. (FromHeader 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

Fields Summary
private Request
lastRequest
Last request.
private boolean
eventPending
Flag indicating events are pending.
private int
viaPort
Via port number.
private String
viaHost
Via host.
private SIPServerResponseInterface
respondTo
Real ResponseInterface to pass messages to.
Constructors Summary
protected ClientTransaction(SIPTransactionStack newSIPMessageStack, MessageChannel newChannelToHeaderUse)
Creates a new client transaction.

param
newSIPMessageStack Transaction stack this transaction belongs to.
param
newChannelToHeaderUse Channel to encapsulate.

        super(newSIPMessageStack, newChannelToHeaderUse);
        setBranch(SIPUtils.generateBranchId());

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "Creating clientTransaction " + this);
            // new Exception().printStackTrace();
        }
    
Methods Summary
public synchronized voidclearEventPending()
Clears the event pending flag.

        eventPending = false;
        notify();
    
public gov.nist.siplite.stack.ClientTransactioncloneWithNewLastResponse(Response lastResponse)
Create a new client transaction based on current. Field lastResponse is filled by input parameter.

param
lastResponse last response
return
new instance of client transaction.

        ClientTransaction clientTransaction = new ClientTransaction(
            (SIPTransactionStack)getSIPStack(), getMessageChannel());
        clientTransaction.lastResponse = lastResponse;
        clientTransaction.setOriginalRequest(getOriginalRequest());
        return clientTransaction;
    
public RequestcreateAck()
Creates an ACK request for this transaction

return
an ack request generated from the original request.
throws
SipException if transaction is in the wrong state to be acked.

        Request originalRequest = getOriginalRequest();
        int statusCode = 0;

        if (originalRequest.getMethod().equals(Request.ACK)) {
            throw new SipException("Cannot ACK an ACK!",
                SipException.INVALID_OPERATION);
        } else if (lastResponse == null) {
            throw new SipException("bad Transaction state",
                SipException.INVALID_STATE);
        } else {
            statusCode = lastResponse.getStatusCode();
            if (statusCode < 200) {
                if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                    Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                        "lastResponse = " + lastResponse);
                }

                throw new SipException("Cannot ACK a provisional response!",
                    SipException.INVALID_OPERATION);
            }
        }

        Request ackRequest =
            originalRequest.createAckRequest((ToHeader)lastResponse.getTo());

        // Automatic ACK at transaction layer
        if (300 <= statusCode && statusCode <= 699) {
            ViaHeader topmostVia = originalRequest.getTopmostVia();
            if (topmostVia != null) {
                ackRequest.setHeader(topmostVia);
            }
            return ackRequest;
        }

        // Pull the record route headers from the last reesponse.
        buildRouteSet(ackRequest);
        return ackRequest;


    
public RequestcreateCancel()
Creates a new Cancel message from the Request associated with this client transaction. The CANCEL request, is used to cancel the previous request sent by this client transaction. Specifically, it asks the UAS to cease processing the request and to generate an error response to that request.

return
a cancel request generated from the original request.
throws
SipException if the request cannot be cancelled.

        Request originalRequest = getOriginalRequest();
        return originalRequest.createCancelRequest();
    
protected voidfireRetransmissionTimer()
Called by the transaction stack when a retransmission timer fires.

        boolean noSend = false;
        try {
            // Resend the last request sent
            // System.out.println("fireRetransmissionTimer ");
            if (this.getState() == -1) {
                noSend = true;
            } else {
                MessageProcessor mp = this.getMessageProcessor();
                if (mp == null) {
                    noSend = true;
                } else if (mp.toExit()) {
                    noSend = true;
                }
            }
            int currentState = this.getState();
            if (!noSend && (currentState == CALLING_STATE ||
                currentState == TRYING_STATE)) {
                getMessageChannel().sendMessage(lastRequest);
            }
        } catch (IOException e) {
            raiseErrorEvent(SIPTransactionErrorEvent.TRANSPORT_ERROR);
        }
    
protected voidfireTimeoutTimer()
Called by the transaction stack when a timeout timer fires.

        Dialog dialogImpl = this.getDialog();
        if (getState() == CALLING_STATE ||
                getState() == TRYING_STATE ||
                getState() == PROCEEDING_STATE) {
            // Timeout occured. If this is asociated with a transaction
            // creation then kill the dialog.
            if (dialogImpl != null) {
                if (((SIPTransactionStack)getSIPStack()).isDialogCreated
                        (this.getOriginalRequest().getMethod())) {
                    // terminate the enclosing dialog.
                    dialogImpl.setState(Dialog.TERMINATED_STATE);
                } else if (getOriginalRequest().getMethod().equals
                        (Request.BYE)) {
                    // Terminate the associated dialog on BYE Timeout.
                    dialogImpl.setState(Dialog.TERMINATED_STATE);
                }
            }
        }
        if (getState() != COMPLETED_STATE) {
            raiseErrorEvent(SIPTransactionErrorEvent.TIMEOUT_ERROR);
        } else {
            setState(TERMINATED_STATE);
        }
    
public ViaHeadergetOutgoingViaHeader()
Gets the via header for an outgoing request.

return
the via header reader

        return this.getMessageProcessor().getViaHeader();
    
public java.lang.StringgetProcessingInfo()
Gets the processing information.

return
processing information

        return respondTo.getProcessingInfo();
    
public MessageChannelgetRequestChannel()
Returns this transaction.

return
request channel transaction

        return this;
    
public java.lang.StringgetViaHost()
Gets the host of the recipient.

return
the via host

 return this.viaHost; 
public intgetViaPort()
Gets the port of the recipient.

return
the via port

 return this.viaPort; 
private voidinviteClientTransaction(Response transactionResponse, MessageChannel sourceChannel)
Implements the state machine for invite client transactions.

param
transactionResponse -- transaction response received.
param
sourceChannel - source channel on which the response was received.

|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|<--------------+
| |
+-----------+

        int statusCode = transactionResponse.getStatusCode();
        int currentState = getState();

        if (currentState == TERMINATED_STATE) {
            // Do nothing in the terminated state.
            return;
        } else if (currentState == CALLING_STATE) {
            if (statusCode/100 == 2) {
                // 200 responses are always seen by TU.
                respondTo.processResponse(transactionResponse, this);
                disableRetransmissionTimer();
                disableTimeoutTimer();
                setState(TERMINATED_STATE);
            } else if (statusCode/100 == 1) {
                disableRetransmissionTimer();
                disableTimeoutTimer();
                respondTo.processResponse(transactionResponse, this);
                setState(PROCEEDING_STATE);
            } else if (300 <= statusCode && statusCode <= 699) {
                // 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.

                respondTo.processResponse(transactionResponse, this);

                // Send back an ACK request
                try {
                    sendMessage((Request) createAck());
                } catch (SipException ex) {
                    InternalErrorHandler.handleException(ex);
                }
                if (! isReliable()) {
                    setState(COMPLETED_STATE);
                    enableTimeoutTimer(TIMER_D);
                } else {
                    // Proceed immediately to the TERMINATED state.
                    setState(TERMINATED_STATE);
                }
            }
        } else if (currentState == PROCEEDING_STATE) {
            if (statusCode / 100 == 1) {
                respondTo.processResponse(transactionResponse, this);
            } else if (statusCode / 100 == 2) {
                setState(TERMINATED_STATE);
                respondTo.processResponse(transactionResponse, this);
            } else if (300 <= statusCode && statusCode <= 699) {
                respondTo.processResponse(transactionResponse, this);
                // Send back an ACK request
                try {
                    sendMessage((Request)createAck());
                } catch (SipException ex) {
                    InternalErrorHandler.handleException(ex);
                }
                if (! isReliable()) {
                    setState(COMPLETED_STATE);
                    enableTimeoutTimer(TIMER_D);
                } else {
                    setState(TERMINATED_STATE);
                }
            }
        } else if (currentState == COMPLETED_STATE) {
            if (300 <= statusCode && statusCode <= 699) {
                // Send back an ACK request
                try {
                    sendMessage((Request)createAck());
                } catch (SipException ex) {
                    InternalErrorHandler.handleException(ex);
                }
            }

        }

    
public booleanisMessagePartOfTransaction(Message messageToHeaderTest)
Deterines if the message is a part of this transaction.

param
messageToHeaderTest Message to check if it is part of this transaction.
return
True if the message is part of this transaction, false if not.

        return isMessageTransOrMult(messageToHeaderTest, false);
    
public booleanisMessageTransOrMult(Message messageToHeaderTest)
Deterines if the message is a part of this transaction or it is multiple 2xx response.

param
messageToHeaderTest Message to check if it is part of this transaction.
return
True if the message is part of this transaction, false if not.

        return isMessageTransOrMult(messageToHeaderTest, true);
    
private booleanisMessageTransOrMult(Message messageToHeaderTest, boolean checkMultResponse)
Deterines if the message is a part of this transaction or it is multiple 2xx response.

param
messageToHeaderTest Message to check if it is part of this transaction.
param
checkMultResponse flag of checking multiple 2xx response
return
True if the message is part of this transaction, false if not.


        // List of Via headers in the message to test
        ViaList viaHeaders = messageToHeaderTest.getViaHeaders();
        // Flags whether the select message is part of this transaction
        boolean transactionMatches;
        String messageBranch = ((ViaHeader)viaHeaders.getFirst()).getBranch();
        boolean rfc3261Compliant =
            (getBranch() != null) &&
            (messageBranch != null) &&
            getBranch().startsWith(SIPConstants.GENERAL_BRANCH_MAGIC_COOKIE) &&
            messageBranch.startsWith(SIPConstants.GENERAL_BRANCH_MAGIC_COOKIE);

        /**
         * if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
         *     Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
         *         "--------- TEST ------------");
         *     Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
         *         " testing " + this.getOriginalRequest());
         *     Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
         *         "Against " + messageToHeaderTest);
         *     Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
         *         "isTerminated = " + isTerminated());
         *     Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
         *         "messageBranch = " + messageBranch);
         *     Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
         *         "viaList = " + messageToHeaderTest.getViaHeaders());
         *     Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
         *        "myBranch = " + getBranch());
         * }
         */

        transactionMatches = false;
        // Response 2xx should be processed even in TERMINATED state
        // RFC 3261, 13.2.2.4:
        // Multiple 2xx responses may arrive at the UAC for a single INVITE
        // request due to a forking proxy.
        boolean isResponse = messageToHeaderTest instanceof Response;
        if (!isTerminated() ||
            (checkMultResponse && isTerminated()
                && isResponse && isInviteTransaction() &&
                (((Response)messageToHeaderTest).getStatusCode()/100 == 2))) {
            if (rfc3261Compliant) {
                if (viaHeaders != null) {
                    // If the branch parameter is the
                    // same as this transaction and the method is the same,
                    if (getBranch().equals
                            (((ViaHeader)viaHeaders.getFirst()).
                            getBranch())) {
                        transactionMatches =
                                getOriginalRequest().getCSeqHeader().
                                getMethod().equals
                                (messageToHeaderTest.getCSeqHeader().
                                getMethod());

                    }
                }
            } else {
                transactionMatches =
                        getOriginalRequest().getTransactionId().equals
                        (messageToHeaderTest.getTransactionId());

            }

        }
        return transactionMatches;

    
public booleanisMultipleResponse(Response response)
Deterines if the response is multiple (RFC 3261, 13.2.2.4).

param
response response for checking transaction.
return
True if the input response has 2xx status, the current transaction has TERMINATED state and the To tag is not same as To tag of current transaction. This method doesn't compare other members, it should be use with method isMessageTransOrMult together .

        boolean returnValue = false;
        if ((response.getStatusCode()/100 == 2) && isTerminated()) {
            Response lastResponse = getLastResponse();
            if (lastResponse != null) {
                String newTag = response.getToTag();
                returnValue = !newTag.equals(lastResponse.getToTag());
            }
        }
        return returnValue;
    
public booleanisSecure()
Checks if connection is secure.

return
true if connection is secure.

 return encapsulatedChannel.isSecure(); 
private voidnonInviteClientTransaction(Response transactionResponse, MessageChannel sourceChannel)
Implements the state machine for invite client transactions.

param
transactionResponse -- transaction response received.
param
sourceChannel - source channel on which the response was received.

|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

        int currentState = getState();

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "nonInviteClientTransaction " +
                transactionResponse.getFirstLine());
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "currentState = " + currentState);
        }

        int statusCode = transactionResponse.getStatusCode();
        if (currentState == TRYING_STATE) {
            if (statusCode / 100 == 1) {
                // Response to TU, RFC 3261, 17.1.4, figure 6
                respondTo.processResponse(transactionResponse, this);
                setState(PROCEEDING_STATE);
                enableRetransmissionTimer
                        (MAXIMUM_RETRANSMISSION_TICK_COUNT);
                enableTimeoutTimer(TIMER_F);
            } else if (200 <= statusCode && statusCode <= 699) {
                // Send the response up to the TU.
                respondTo.processResponse(transactionResponse, this);
                if (! isReliable()) {
                    setState(COMPLETED_STATE);
                    enableTimeoutTimer(TIMER_K);
                } else {
                    setState(TERMINATED_STATE);
                }
            }
        } else if (currentState == PROCEEDING_STATE &&
                200 <= statusCode && statusCode <= 699) {

            respondTo.processResponse(transactionResponse, this);

            disableRetransmissionTimer();
            disableTimeoutTimer();
            if (! isReliable()) {
                setState(COMPLETED_STATE);
                enableTimeoutTimer(TIMER_K);
            } else {
                setState(TERMINATED_STATE);
	        }
        } else if (currentState == PROCEEDING_STATE &&
                statusCode / 100 == 1) {
		    // Response to TU, RFC 3261, 17.1.4, figure 6
            respondTo.processResponse(transactionResponse, this);
        }
    
public synchronized voidprocessResponse(Response transactionResponse, MessageChannel sourceChannel)
Processes a new response message through this transaction. If necessary, this message will also be passed onto the TU.

param
transactionResponse Response to process.
param
sourceChannel Channel that received this message.


        // Log the incoming response in our log file.
        if (ServerLog.needsLogging(ServerLog.TRACE_MESSAGES))
            this.logResponse(transactionResponse,
                    System.currentTimeMillis(), "normal processing");

        int statusGroup = transactionResponse.getStatusCode()/100;

        // Ignore 1xx
        if (getState() == COMPLETED_STATE && statusGroup == 1) {
            return;
        // This block is against RFC 3261 17.1.1.2, figure 5 and
        // 17.1.4, figure 6
        /*
        } else if (PROCEEDING_STATE == this.getState()
        && transactionResponse.getStatusCode() == 100) {
            // Ignore 100 if received after 180
            return;
        */
        } else {
            // IMPL_NOTE: investigate if this flag may be completely removed.
            while (eventPending) {
                try {
                    // Wait for clearEventPending() call.
                    wait();
                } catch (InterruptedException e) {
                    // intentionally ignored
                    // wait for clearEventPending() call
                }
            }
        }

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "processing " +
                transactionResponse.getFirstLine()
                + "current state = "
                + getState());
        }

        this.lastResponse = transactionResponse;

        if ((dialog != null) && (statusGroup < 3)) {
            // add the route before you process the response.
            dialog.addRoute(transactionResponse);
        }

        String method = transactionResponse.getCSeqHeader().getMethod();
        
        if (dialog != null) {
            boolean added = false;
            SIPTransactionStack sipStackImpl
                    = (SIPTransactionStack) getSIPStack();

            // A tag just got assigned or changed.
            if (dialog.getRemoteTag() == null &&
                    transactionResponse.getTo().getTag() != null) {

                // Dont assign tag on provisional response
                if (transactionResponse.getStatusCode() != 100) {
                    dialog.setRemoteTag(transactionResponse.getToTag());
                }

                String dialogId = transactionResponse.getDialogId(false);
                dialog.setDialogId(dialogId);

                if (sipStackImpl.isDialogCreated(method) &&
                        transactionResponse.getStatusCode() != 100) {
                    sipStackImpl.putDialog(dialog);
                    if (statusGroup == 1) {
                        dialog.setState(Dialog.EARLY_STATE);
                    } else if (statusGroup == 2) {
                        dialog.setState(Dialog.CONFIRMED_STATE);
                    }
                    added = true;
                }

            } else if (dialog.getRemoteTag() != null &&
                    transactionResponse.getToTag() != null &&
                    ! dialog.getRemoteTag().equals
                    (transactionResponse.getToTag())) {
                dialog.setRemoteTag(transactionResponse.getToTag());
                String dialogId = transactionResponse.getDialogId(false);
                dialog.setDialogId(dialogId);

                if (sipStackImpl.isDialogCreated(method)) {
                    sipStackImpl.putDialog(dialog);
                    added = true;
                }
            }

            if (sipStackImpl.isDialogCreated(method)) {
                // Make a final tag assignment.
                if (transactionResponse.getToTag() != null &&
                        statusGroup == 2) {
                    // This is a dialog creating method (such as INVITE).
                    // 2xx response -- set the state to the confirmed
                    // state.
                    dialog.setRemoteTag(transactionResponse.getToTag());
                    dialog.setState(Dialog.CONFIRMED_STATE);
                } else if ((
                        transactionResponse.getStatusCode() == 487 ||
                        statusGroup == 5 ||
                        statusGroup == 6) &&
                        (dialog.getState() == -1 ||
                        dialog.getState() ==
                        Dialog.EARLY_STATE)) {
                    // Invite transaction generated an error.
                    dialog.setState(Dialog.TERMINATED_STATE);
                }
            }
            
            // 200 OK for a bye so terminate the dialog.
            if (transactionResponse.getCSeqHeader().
                    getMethod().equals(Request.BYE) &&
                        transactionResponse.getStatusCode() == 200) {
                dialog.setState(Dialog.TERMINATED_STATE);
            }
        }
        
        try {
            if (isInviteTransaction()) {
                inviteClientTransaction(transactionResponse, sourceChannel);
            } else {
                nonInviteClientTransaction(transactionResponse, sourceChannel);
            }
        } catch (IOException ex) {
            setState(TERMINATED_STATE);
            raiseErrorEvent(SIPTransactionErrorEvent.TRANSPORT_ERROR);
        }
    
public voidsendMessage(Message messageToHeaderSend)
Sends a request message through this transaction and onto the client.

param
messageToHeaderSend Request to process and send.


        // Message typecast as a request
        Request transactionRequest;


        transactionRequest = (Request)messageToHeaderSend;


        // Set the branch id for the top via header.
        ViaHeader topVia =
                (ViaHeader) transactionRequest.getViaHeaders().getFirst();
        // Tack on a branch identifier to match responses.

        topVia.setBranch(getBranch());

        // If this is the first request for this transaction,
        if (getState() == INITIAL_STATE) {
            // Save this request as the one this transaction
            // is handling
            setOriginalRequest(transactionRequest);

            // Change to trying/calling state
            if (transactionRequest.getMethod().equals(Request.INVITE)) {
                setState(CALLING_STATE);
            } else if (transactionRequest.getMethod().equals(Request.ACK)) {
                // Acks are never retransmitted.
                setState(TERMINATED_STATE);
            } else {
                setState(TRYING_STATE);
            }

            if (!isReliable()) {
                enableRetransmissionTimer();
            }

            if (isInviteTransaction()) {
                enableTimeoutTimer(TIMER_B);
            } else {
                enableTimeoutTimer(TIMER_F);
            }

        } else if (getState() == PROCEEDING_STATE ||
                getState() == CALLING_STATE) {

            // If this is a TU-generated ACK request,
            if (transactionRequest.getMethod().equals(Request.ACK)) {
                // Send directly to the underlying
                // transport and close this transaction
                setState(TERMINATED_STATE);
                getMessageChannel().sendMessage(transactionRequest);
                return;

            }

        }

        try {
            // Send the message to the server
            lastRequest = transactionRequest;
            getMessageChannel().sendMessage(transactionRequest);
        } catch (IOException e) {
            setState(TERMINATED_STATE);
            throw e;
        }
    
public voidsendRequest()
Sends specified {@link gov.nist.siplite.message.Request} on a unique client transaction identifier. This method implies that the application is functioning as either a User Agent Client or a Stateful proxy, hence the underlying SipProvider acts statefully.

JAIN SIP defines a retransmission utility specific to user agent behaviour and the default retransmission behaviour for each method.

When an application wishes to send a message, it creates a Request message passes that Request to this method, this method returns the cleintTransactionId generated by the SipProvider. The Request message gets sent via the ListeningPoint that this SipProvider is attached to.

  • User Agent Client - must not send a BYE on a confirmed INVITE until it has received an ACK for its 2xx response or until the server transaction times out.

throws
IOException if an I/O error occured
throws
SipException if implementation cannot send request for any other reason

        Request sipRequest = getOriginalRequest();
        sendMessage(sipRequest);
    
public synchronized voidsetEventPending()
Sets the event pending flag.

        eventPending = true;
    
public voidsetResponseInterface(SIPServerResponseInterface newRespondToHeader)
Sets the real ResponseInterface this transaction encapsulates.

param
newRespondToHeader ResponseInterface to send messages to.

        respondTo = newRespondToHeader;
    
protected voidsetViaHost(java.lang.String host)
Sets the port of the recipient.

param
host the new via host

 this.viaHost = host; 
protected voidsetViaPort(int port)
Sets the port of the recipient.

param
port the new via port

 this.viaPort = port;