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

Transaction

public abstract class Transaction extends MessageChannel
Abstract class to support both client and server transactions. Provides an encapsulation of a message channel, handles timer events, and creation of the Via header for a message.
version
JAIN-SIP-1.1

Fields Summary
protected static final int
BASE_TIMER_INTERVAL
Transaction timer interval.
protected static final int
T1
RTT Estimate. 500ms default.
protected static final int
T4
5 sec Maximum duration a message will remain in the network
protected static final int
T2
The maximum retransmit interval for non-INVITE requests and INVITE responses
protected static final int
TIMER_A
INVITE request retransmit interval, for UDP only
protected static final int
TIMER_B
INVITE transaction timeout timer
protected static final int
TIMER_J
INVITE transaction timeout timer
protected static final int
TIMER_F
INVITE transaction timeout timer
protected static final int
TIMER_H
INVITE transaction timeout timer
protected static final int
TIMER_I
INVITE transaction timeout timer
protected static final int
TIMER_K
INVITE transaction timeout timer
protected static final int
TIMER_D
INVITE transaction timeout timer
protected static final int
TIMER_C
INVITE transaction timeout timer
protected Response
lastResponse
Last response message.
protected Dialog
dialog
Current SIP dialog.
protected boolean
ackSeenFlag
Flag indicating an ACK was received.
protected boolean
toListener
Flag indicating listener waiting.
public static final int
INITIAL_STATE
Initialized but no state assigned.
public static final int
TRYING_STATE
Trying state.
public static final int
CALLING_STATE
CALLING State.
public static final int
PROCEEDING_STATE
Proceeding state.
public static final int
COMPLETED_STATE
Completed state.
public static final int
CONFIRMED_STATE
Confirmed state.
public static final int
TERMINATED_STATE
Terminated state.
protected static final int
MAXIMUM_RETRANSMISSION_TICK_COUNT
Maximum number of ticks between retransmissions.
protected SIPTransactionStack
parentStack
Parent stack for this transaction.
private Request
originalRequest
Original request that is being handled by this transaction.
protected MessageChannel
encapsulatedChannel
Underlying channel being used to send messages for this transaction.
private String
branch
Transaction branch ID.
private int
currentState
Current transaction state.
private int
retransmissionTimerLastTickCount
Number of ticks the retransmission timer was set to last.
private int
retransmissionTimerTicksLeft
Number of ticks before the message is retransmitted.
private int
timeoutTimerTicksLeft
Number of ticks before the transaction times out.
private Vector
eventListeners
List of event listeners for this transaction.
protected boolean
isCancelled
Flag to indcate that this has been cancelled.
protected Object
applicationData
Object representing the connection being held by the JSR180 Implementation It can be either a SipClientConnection in case of a ClientTransaction or a SipConnectionNotifier in case of a ServerTransaction.
Constructors Summary
protected Transaction(SIPTransactionStack newParentStack, MessageChannel newEncapsulatedChannel)
Transaction constructor.

param
newParentStack Parent stack for this transaction.
param
newEncapsulatedChannel Underlying channel for this transaction.

        parentStack = newParentStack;
        encapsulatedChannel = newEncapsulatedChannel;
        currentState = INITIAL_STATE;
        disableRetransmissionTimer();
        disableTimeoutTimer();
        eventListeners = new Vector();
        // Always add the parent stack as a listener
        // of this transaction
        addEventListener(newParentStack);
    
Methods Summary
protected booleanIsServerTransaction()
A shortcut way of telling if we are a server transaction.

return
true if this is a servertransaction

        return this instanceof ServerTransaction;
    
public voidaddEventListener(SIPTransactionEventListener newListener)
Adds a new event listener to this transaction.

param
newListener Listener to add.

        eventListeners.addElement(newListener);
    
protected voidbuildRouteSet(Request request)
Create route set for request.

param
request the input request
throws
SipException if any occurs

        Message origMsg = lastResponse;
        boolean isServer = false;
        Dialog dlg = getDialog();

        if (dlg != null) { // inside of dialog
            Transaction firstTransaction = dlg.getFirstTransaction();
            if (dlg.isServer()) { // dialog was created on server side
               origMsg = firstTransaction.getOriginalRequest();
               isServer = true;
           } else { // dialog was created on client side
               origMsg = firstTransaction.getLastResponse();
           }
        } else { // out of dialog
            // IMPL_NOTE: implement a support for the case when Contact
            // header is not present.
            if (this instanceof ServerTransaction) {
                origMsg = getOriginalRequest();
                isServer = true;
            }
        }

        if (origMsg == null) {
            // Avoid NPE in case when sending ACK from the part that
            // initiated a Re-INVITE.
            return;
        }

        RecordRouteList recordRouteList = origMsg.getRecordRouteHeaders();
        ContactList cl = origMsg.getContactHeaders();

        if (cl == null) {
            // Prevent NPE and log an error: Contact header is absent.
            if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                    "Transaction.buildRouteSet(): Contact must be present!");
            }
            return;
        }

        ContactHeader contact = (ContactHeader)cl.getFirst();

        if (recordRouteList == null) {
            URI remoteTarget = contact.getAddress().getURI();

            // RFC 3261, 12.2.1.1 Generating the Request
            // If the route set is empty, the UAC MUST place the remote
            // target URI into the Request-URI.  The UAC MUST NOT add
            // a Route header field to the request.
            request.setRequestURI(remoteTarget);
        } else {
            request.removeHeader(Header.ROUTE);

            RouteList routeList = new RouteList();
            // start at the end of the list and walk backwards
            Vector li = recordRouteList.getHeaders();

            int recSize = li.size();
            for (int i = 0; i < recSize; i++) {
                int j = i;
                if (!isServer) { // on client side the order is reversed
                    j = recSize - i - 1;
                }
                RecordRouteHeader rr = (RecordRouteHeader) li.elementAt(j);
                Address addr = rr.getAddress();
                RouteHeader route = new RouteHeader();
                route.setAddress((Address)rr.getAddress().clone());
                route.setParameters((NameValueList)rr.getParameters().clone());
                routeList.add(route);
            }

            RouteHeader firstRoute = (RouteHeader) routeList.getFirst();

            if (!((SipURI)firstRoute.getAddress().getURI()).hasLrParam()) {
                RouteHeader route = new RouteHeader();
                route.setAddress((Address)contact.getAddress().clone());
                routeList.removeFirst();
                // IMPL_NOTE: is clone() need?
                URI uri = firstRoute.getAddress().getURI();
                request.setRequestURI(uri);
                routeList.add(route);
                request.addHeader(routeList);
            } else {
                URI uri = (URI) contact.getAddress().getURI().clone();
                request.setRequestURI(uri);
                request.addHeader(routeList);
            }
        }
    
public voidclose()
Closes the encapsulated channel.

        encapsulatedChannel.close();
    
protected final voiddisableRetransmissionTimer()
Turns off retransmission events for this transaction.

        retransmissionTimerTicksLeft = -1;
    
protected final voiddisableTimeoutTimer()
Disabled the timeout timer.

        timeoutTimerTicksLeft = -1;
    
public booleandoesCancelMatchTransaction(Request requestToHeaderTest)
A method that can be used to test if an incoming request belongs to this transction. This does not take the transaction state into account when doing the check otherwise it is identical to isMessagePartOfTransaction. This is useful for checking if a CANCEL belongs to this transaction.

param
requestToHeaderTest is the request to test.
return
true if the the request belongs to the transaction.

        // List of Via headers in the message to test
        ViaList viaHeaders;
        // ToHeaderpmost Via header in the list
        ViaHeader topViaHeader;
        // Branch code in the topmost Via header
        String messageBranch;
        // Flags whether the select message is part of this transaction
        boolean transactionMatches;
        transactionMatches = false;

        if (getOriginalRequest() == null ||
                getOriginalRequest().getMethod().equals(Request.CANCEL)) {
            return false;
        }

        // Get the topmost Via header and its branch parameter
        viaHeaders = requestToHeaderTest.getViaHeaders();
        if (viaHeaders != null) {
            topViaHeader = (ViaHeader)viaHeaders.getFirst();
            messageBranch = topViaHeader.getBranch();
            if (messageBranch != null) {
                // If the branch parameter exists but
                // does not start with the magic cookie,
                if (!messageBranch.toUpperCase().startsWith(SIPConstants.
                        GENERAL_BRANCH_MAGIC_COOKIE.toUpperCase())) {
                    // Flags this as old
                    // (RFC2543-compatible) client
                    // version
                    messageBranch = null;
                }
            }
            // If a new branch parameter exists,
            if (messageBranch != null &&
                    this.getBranch() != null) {
                // If the branch equals the branch in
                // this message,
                if (getBranch().equals(messageBranch)
                && topViaHeader.getSentBy().
                        equals(((ViaHeader)getOriginalRequest().
                        getViaHeaders().getFirst()).
                        getSentBy())) {
                    transactionMatches = true;

                    if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                        Logging.report(Logging.INFORMATION,
                            LogChannels.LC_JSR180, "returning true");
                    }
                }
            } else {
                // If this is an RFC2543-compliant message,
                // If RequestURI, ToHeader tag, FromHeader tag,
                // CallIdHeader, CSeqHeader number, and top Via
                // headers are the same,
                if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                    Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                        "testing against " + getOriginalRequest());
                }

                if (

                        getOriginalRequest().getRequestURI().equals
                        (requestToHeaderTest.getRequestURI()) &&
                        getOriginalRequest().getTo().equals
                        (requestToHeaderTest.getTo()) &&
                        getOriginalRequest().getFromHeader().equals
                        (requestToHeaderTest.getFromHeader()) &&
                        getOriginalRequest().getCallId().
                        getCallId().equals
                        (requestToHeaderTest.getCallId() .getCallId()) &&
                        getOriginalRequest().
                        getCSeqHeader().getSequenceNumber() ==
                        requestToHeaderTest.getCSeqHeader().
                        getSequenceNumber() &&
                        topViaHeader.equals
                        (getOriginalRequest().
                        getViaHeaders().getFirst())) {
                    transactionMatches = true;
                }
            }
        }
        return transactionMatches;
    
protected final voidenableRetransmissionTimer()
Enables retransmission timer events for this transaction to begin in one tick.

        enableRetransmissionTimer(1);
    
protected final voidenableRetransmissionTimer(int tickCount)
Enables retransmission timer events for this transaction to begin after the number of ticks passed to this routine.

param
tickCount Number of ticks before the next retransmission timer event occurs.

        retransmissionTimerTicksLeft =
                Math.min(tickCount, MAXIMUM_RETRANSMISSION_TICK_COUNT);
        retransmissionTimerLastTickCount =
                retransmissionTimerTicksLeft;
    
protected final voidenableTimeoutTimer(int tickCount)
Enables a timeout event to occur for this transaction after the number of ticks passed to this method.

param
tickCount Number of ticks before this transaction times out.

        timeoutTimerTicksLeft = tickCount;
    
protected abstract voidfireRetransmissionTimer()
This method is called when this transaction's retransmission timer has fired.

protected abstract voidfireTimeoutTimer()
This method is called when this transaction's timeout timer has fired.

final synchronized voidfireTimer()
Fired after each timer tick. Checks the retransmission and timeout timers of this transaction, and fired these events if necessary.

        // If the timeout timer is enabled,
        if (timeoutTimerTicksLeft != -1) {
            // Count down the timer, and if it has run out,
            if (--timeoutTimerTicksLeft == 0) {
                // Fire the timeout timer
                fireTimeoutTimer();
            }
        }
        // If the retransmission timer is enabled,
        if (retransmissionTimerTicksLeft != -1) {
            // Count down the timer, and if it has run out,
            if (--retransmissionTimerTicksLeft == 0) {
                // Enable this timer to fire again after
                // twice the original time
                enableRetransmissionTimer
                        (retransmissionTimerLastTickCount * 2);
                // Fire the timeout timer
                fireRetransmissionTimer();
            }
        }
    
public java.lang.ObjectgetApplicationData()
Retrieves the application data.

return
Object representing the connection being held by the JSR180 Implementation. It can be either a SipClientConnection in case of a ClientTransaction or a SipConnectionNotifier in case of a ServerTransaction


                                           
       
        return applicationData;
    
public final java.lang.StringgetBranch()
Gets the current setting for the branch parameter of this transaction.

return
Branch parameter for this transaction.

        if (branch == null) {
            branch = getOriginalRequest().getTopmostVia().getBranch();
        }
        return branch;
    
public java.lang.StringgetBranchId()
Gets the branch identifier.

return
the current branch id

        return branch;
    
public DialoggetDialog()
Gets the dialog object of this Transaction object. This object returns null if no dialog exists. A dialog only exists for a transaction when a session is setup between a User Agent Client and a User Agent Server, either by a 1xx Provisional Response for an early dialog or a 200OK Response for a committed dialog.

return
the Dialog Object of this Transaction object.
see
Dialog

        return dialog;
    
public java.lang.StringgetHost()
Gets the host.

return
the host

        return encapsulatedChannel.getHost();
    
public java.lang.StringgetKey()
Gets the key.

return
the key

        return encapsulatedChannel.getKey();
    
public ResponsegetLastResponse()
Gets the last response.

return
the last response

 return this.lastResponse; 
public MessageChannelgetMessageChannel()
Returns the message channel used for transmitting/receiving messages for this transaction. Made public in support of JAIN dual transaction model.

return
Encapsulated MessageChannel.

        return encapsulatedChannel;
    
public MessageProcessorgetMessageProcessor()
Gets the message processor handling this transaction.

return
the mesage processor for this transaction

        return encapsulatedChannel.getMessageProcessor();
    
public RequestgetOriginalRequest()
Gets the request being handled by this transaction.

return
Request being handled.

        return originalRequest;
    
public java.lang.StringgetPeerAddress()
Gets the remote address.

return
the remote address

        return encapsulatedChannel.getPeerAddress();
    
public intgetPeerPort()
Gets the remote port number.

return
the remote port number

        return encapsulatedChannel.getPeerPort();
    
public intgetPort()
Gets the port.

return
the port

        return encapsulatedChannel.getPort();
    
public RequestgetRequest()
Gets the original request but cast to a Request structure.

return
the request that generated this transaction.

        return (Request) originalRequest;
    
public intgetRetransmitTimer()
Returns the current value of the retransmit timer in milliseconds used to retransmit messages over unreliable transports.

return
the integer value of the retransmit timer in milliseconds.

        return SIPTransactionStack.BASE_TIMER_INTERVAL;
    
public SIPMessageStackgetSIPStack()
Gets the SIP stack context.

return
the SIP Stack

        return parentStack;
    
public final intgetState()
Gets the current state of this transaction.

return
Current state of this transaction.

        return currentState;
    
public java.lang.StringgetTransactionId()
Gets the transaction Id.

return
the transaction id

        return getOriginalRequest().getTransactionId();
    
public java.lang.StringgetTransport()
Gets the connection transport.

return
the connection transport

        return encapsulatedChannel.getTransport();
    
public ViaHeadergetViaHeader()
Returns the Via header for this channel. Gets the Via header of the underlying message channel, and adds a branch parameter to it for this transaction.

return
the via header

        // Via header of the encapulated channel
        ViaHeader channelViaHeader;
        // Add the branch parameter to the underlying
        // channel's Via header
        channelViaHeader = super.getViaHeader();
        channelViaHeader.setBranch(branch);
        return channelViaHeader;
    
public java.lang.StringgetViaHost()
Gets the host to assign for an outgoing Request via header.

return
the via host

        return getViaHeader().getHost();
    
public intgetViaPort()
Gets the port to assign for the via header of an outgoing message.

return
the via port number

        return getViaHeader().getPort();
    
public voidhandleException(SIPServerException ex)
Process an exception.

param
ex the exception to handle

        encapsulatedChannel.handleException(ex);
    
public booleanisAckSeen()
Checks if the ACK has been seen flag is set.

return
true if the ACK has been seen

        return ackSeenFlag;
    
protected final booleanisByeTransaction()
Returns a flag that states if this is a BYE transaction.

return
true if the transaciton is a BYE transaction.

        return originalRequest.getMethod().equals(Request.BYE);
    
protected final booleanisCancelTransaction()
Returns true if the transaction corresponds to a CANCEL message.

return
true if the transaciton is a CANCEL transaction.

        return originalRequest.getMethod().equals(Request.CANCEL);
    
protected final booleanisInviteTransaction()
Returns a flag stating whether this transaction is for an INVITE request or not.

return
True if this is an INVITE request, false if not.

        return originalRequest.getMethod().equals(Request.INVITE);
    
public abstract booleanisMessagePartOfTransaction(Message messageToHeaderTest)
Tests a message to see if it is part of this transaction.

param
messageToHeaderTest message to be processed
return
True if the message is part of this transaction, false if not.

public booleanisReliable()
Checks if the connection is reliable

return
true if channel is on a stream connection

        return encapsulatedChannel.isReliable();
    
public booleanisSecure()
Check if this connection is secure.

return
true if this is a secure channel

        return encapsulatedChannel.isSecure();
    
protected final booleanisTerminated()
Tests if this transaction has terminated.

return
Trus if this transaction is terminated, false if not.

        return (getState() == TERMINATED_STATE);
    
public booleanpassToListener()
Checks if transaction has been sent to the listener.

return
true if transaction has been sent

        return toListener;
    
protected voidraiseErrorEvent(int errorEventID)
Creates a SIPTransactionErrorEvent and sends it to all of the listeners of this transaction. This method also flags the transaction as terminated.

param
errorEventID ID of the error to raise.

        // Error event to send to all listeners
        SIPTransactionErrorEvent newErrorEvent;
        // Iterator through the list of listeners
        Enumeration listenerIterator;
        // Next listener in the list
        SIPTransactionEventListener nextListener;
        // Create the error event
        newErrorEvent = new SIPTransactionErrorEvent(this,
                errorEventID);
        // Loop through all listeners of this transaction
        synchronized (eventListeners) {
            listenerIterator = eventListeners.elements();
            while (listenerIterator.hasMoreElements()) {
                // Send the event to the next listener
                nextListener = (SIPTransactionEventListener)
                listenerIterator.nextElement();
                nextListener.transactionErrorEvent
                        (newErrorEvent);
            }
        }
        // Clear the event listeners after propagating the error.
        eventListeners.removeAllElements();
        // Errors always terminate a transaction
        setState(TERMINATED_STATE);
        if (this instanceof ServerTransaction &&
                this.isByeTransaction() && this.dialog != null)
            this.dialog.setState(Dialog.TERMINATED_STATE);
    
public voidremoveEventListener(SIPTransactionEventListener oldListener)
Removes an event listener from this transaction.

param
oldListener Listener to remove.

        eventListeners.removeElement(oldListener);
    
public abstract voidsendMessage(Message messageToHeaderSend)
Processes the message through the transaction and sends it to the SIP peer.

param
messageToHeaderSend Message to send to the SIP peer.

protected voidsendMessage(byte[] messageBytes, java.lang.String receiverAddress, int receiverPort)
Parses the byte array as a message, process it through the transaction, and send it to the SIP peer.

param
messageBytes Bytes of the message to send.
param
receiverAddress Address of the target peer.
param
receiverPort Network port of the target peer.
throws
IOException If there is an error parsing the byte array into an object.

        // Object representation of the SIP message
        Message messageToHeaderSend;
        try {
            StringMsgParser messageParser = new StringMsgParser();
            messageToHeaderSend =
                    messageParser.parseSIPMessage(messageBytes);
            sendMessage(messageToHeaderSend);
        } catch (ParseException e) {
            throw new IOException(e.getMessage());
        }
    
public voidsetAckSeen()
Sets the ACK has been seen flag.

        ackSeenFlag = true;
    
public voidsetApplicationData(java.lang.Object newApplicationData)
Sets the application data.

param
newApplicationData Object representing the connection being held by the JSR180 Implementation. It can be either a SipClientConnection in case of a ClientTransaction or a SipConnectionNotifier in case of a ServerTransaction

        applicationData = newApplicationData;
    
public final voidsetBranch(java.lang.String newBranch)
Sets the Via header branch parameter used to identify this transaction.

param
newBranch New string used as the branch for this transaction.

        branch = newBranch;
    
public voidsetDialog(Dialog newDialog)
Sets the dialog object.

param
newDialog the dialog to set.

        dialog = newDialog;
    
public voidsetOriginalRequest(Request newOriginalRequest)
Sets the request message that this transaction handles.

param
newOriginalRequest Request being handled.

        // Branch value of topmost Via header
        String newBranch;

        originalRequest = newOriginalRequest;
        originalRequest.setTransaction(this);
        // If the message has an explicit branch value set,
        newBranch = ((ViaHeader)newOriginalRequest.getViaHeaders().
                getFirst()).getBranch();
        if (newBranch != null) {
            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "Setting Branch id : " + newBranch);
            }

            // Override the default branch with the one
            // set by the message
            setBranch(newBranch);
        } else {
            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "Branch id is null!" + newOriginalRequest.encode());
            }
        }
    
public voidsetState(int newState)
Changes the state of this transaction.

param
newState New state of this transaction.

        currentState = newState;

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "setState " + this + " " + newState);
        }

        // If this transaction is being terminated,
        if (newState == TERMINATED_STATE) {
            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "Transaction is being terminated!");
                // new Exception().printStackTrace();
            }
        }