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

Dialog

public class Dialog extends Object
Tracks dialogs. A dialog is a peer to peer association of communicating SIP entities. For INVITE transactions, a Dialog is created when a success message is received (i.e. a response that has a ToHeader tag). The SIP Protocol stores enough state in the message structure to extract a dialog identifier that can be used to retrieve this structure from the SipStack.
version
JAIN-SIP-1.1 This code is in the public domain.

Fields Summary
private Object
applicationData
Opaque pointer to application data.
private Transaction
firstTransaction
First transaction.
private Transaction
lastTransaction
Last transaction.
private String
dialogId
Dialog identifier.
private int
localSequenceNumber
Local sequence number.
private int
remoteSequenceNumber
Remote sequence number.
private String
myTag
Local tag.
private String
hisTag
Remote tag.
private RouteList
routeList
Route list.
private RouteHeader
contactRoute
Contact route.
private String
user
User name.
private RouteHeader
defaultRoute
Default route.
private SIPTransactionStack
sipStack
Current context SIP stack.
private int
dialogState
Current Dialog state.
protected boolean
ackSeen
ACK has been processed.
protected Request
lastAck
Previous ACk.
public SubscriptionList
subscriptionList
List of active subscriptions.
private int
retransmissionTicksLeft
Time remaining for retransmit.
private int
prevRetransmissionTicks
Time used on previous retransmit.
public static final int
INITIAL_STATE
Initial state.
public static final int
EARLY_STATE
Early state.
public static final int
CONFIRMED_STATE
Confirmed state.
public static final int
COMPLETED_STATE
Completed state.
public static final int
TERMINATED_STATE
Terminated state.
Constructors Summary
private Dialog()
Protected Dialog constructor.

        subscriptionList = new SubscriptionList();
        routeList = new RouteList();
        dialogState = -1; // not yet initialized.
        localSequenceNumber = 0;
        remoteSequenceNumber = -1;
    
protected Dialog(Transaction transaction)
Constructor given the first transaction.

param
transaction is the first transaction.

        this();
        addTransaction(transaction);
    
Methods Summary
public voidackReceived(Request sipRequest)
Marks that the dialog has seen an ACK.

param
sipRequest the current SIP transaction

        if (isServer()) {
            ServerTransaction st = (ServerTransaction) getFirstTransaction();
            if (st == null) {
                return;
            }

            // Suppress retransmission of the final response (in case
            // retransmission filter is being used).
            if (st.getOriginalRequest().getCSeqHeader().getSequenceNumber() ==
                    sipRequest.getCSeqHeader().getSequenceNumber()) {
                st.setState(Transaction.TERMINATED_STATE);
                ackSeen = true;
                lastAck = sipRequest;
            }
        }
    
private voidaddRoute(RecordRouteList recordRouteList, java.lang.String transport)
Adds a route list extracted from a record route list. If this is a server dialog then we assume that the record are added to the route list IN order. If this is a client dialog then we assume that the record route headers give us the route list to add in reverse order.

param
recordRouteList -- the record route list from the incoming
param
transport -- the transport parameter message.

        if (this.isClientDialog()) {
            // This is a client dialog so we extract the record
            // route from the response and reverse its order to
            // careate a route list.
            this.routeList = new RouteList();
            // start at the end of the list and walk backwards
            Vector li = recordRouteList.getHeaders();
            for (int i = li.size() -1; i >= 0; i--) {
                RecordRouteHeader rr = (RecordRouteHeader) li.elementAt(i);
                Address addr = (Address) rr.getAddress();
                RouteHeader route = new RouteHeader();
                route.setAddress
                        ((Address)(rr.getAddress()).clone());
                route.setParameters
                        ((NameValueList)rr.getParameters().clone());
                setRouteTransport(route, transport);
                this.routeList.add(route);
            }
        } else {
            // This is a server dialog. The top most record route
            // header is the one that is closest to us. We extract the
            // route list in the same order as the addresses in the
            // incoming request.
            this.routeList = new RouteList();
            Vector li = recordRouteList.getHeaders();
            for (int i = 0; i < li.size(); i++) {
                RecordRouteHeader rr = (RecordRouteHeader) li.elementAt(i);
                RouteHeader route = new RouteHeader();
                route.setAddress
                        ((Address)(rr.getAddress()).clone());
                route.setParameters((NameValueList)rr.getParameters().
                        clone());
                setRouteTransport(route, transport);
                routeList.add(route);
            }
        }
    
private voidaddRoute(ContactList contactList, java.lang.String transport)
Adds a route list extacted from the contact list of the incoming message.

param
contactList contact list extracted from the incoming message.
param
transport the transport parameter

        if (contactList.size() == 0)
            return;
        ContactHeader contact = (ContactHeader) contactList.getFirst();
        RouteHeader route = new RouteHeader();
        route.setAddress
                ((Address)((Address)(contact.getAddress())).clone());
        setRouteTransport(route, transport);
        this.contactRoute = route;
    
public synchronized voidaddRoute(Message sipMessage)
Extracts the route information from this SIP Message and add the relevant information to the route set.

param
sipMessage is the SIP message for which we want to add the route.

        String method = sipMessage.getCSeqHeader().getMethod();

        // cannot add route list after the dialog is initialized.
        try {
            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "addRoute: dialogState: " + this + "state = " +
                    this.getState());
            }

            String transport =
                ((ViaHeader)(sipMessage.getViaHeaders().getFirst())).
                    getTransport();

            // UPDATE processing: change route according to contact list
            if (Request.UPDATE.equals(method) &&
                ((this.dialogState == EARLY_STATE) ||
                    (this.dialogState == CONFIRMED_STATE))) {

                ContactList contactList = sipMessage.getContactHeaders();
                if (contactList != null) {
                    this.addRoute(contactList, transport);
                }
                return;
            }

            if (this.dialogState == CONFIRMED_STATE ||
                    this.dialogState == COMPLETED_STATE ||
                    this.dialogState == TERMINATED_STATE) {
                return;
            }

            if (!isServer()) {
                // I am CLIENT dialog.
                if (sipMessage instanceof Response) {
                    Response sipResponse = (Response) sipMessage;
                    if (sipResponse.getStatusCode() == 100) {
                        // Do nothing for trying messages.
                        return;
                    }

                    RecordRouteList rrlist = sipMessage.getRecordRouteHeaders();

                    // Add the route set from the incoming response in reverse
                    // order
                    if (rrlist != null) {
                        this.addRoute(rrlist, transport);
                    } else {
                        // Set the rotue list to the last seen route list.
                        this.routeList = new RouteList();
                    }

                    ContactList contactList = sipMessage.getContactHeaders();
                    if (contactList != null) {
                        if (sipResponse.getStatusCode()/100 == 2 &&
                                (dialogState == INITIAL_STATE ||
                                        dialogState == EARLY_STATE)) {
                            ContactHeader contact =
                                    (ContactHeader)contactList.getFirst();

                            if (contact != null) {
                                String newTransport = contact.getAddress().
                                        getParameter(SIPConstants.GENERAL_TRANSPORT);
                                if (newTransport != null) {
                                    transport = newTransport;
                                }
                            }
                        }
                        
                        this.addRoute(contactList, transport);
                    }
                }
            } else {
                if (sipMessage instanceof Request) {
                    // Incoming Request has the route list
                    RecordRouteList rrlist = sipMessage.getRecordRouteHeaders();
                    // Add the route set from the incoming response in reverse
                    // order
                    if (rrlist != null) {

                        this.addRoute(rrlist, transport);
                    } else {
                        // Set the rotue list to the last seen route list.
                        this.routeList = new RouteList();
                    }
                    // put the contact header from the incoming request into
                    // the route set.
                    ContactList contactList = sipMessage.getContactHeaders();
                    if (contactList != null) {
                        this.addRoute(contactList, transport);
                    }
                }
            }
        } finally {
            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                // new Exception()printStackTrace();
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "added a route = " + routeList.encode() +
                    "contactRoute = " + contactRoute);
            }
        }
    
public voidaddTransaction(Transaction transaction)
Adds a transaction record to the dialog.

param
transaction is the transaction to add to the dialog.

        Request sipRequest = (Request) transaction.getOriginalRequest();

        // Processing a re-invite.
        // IMPL_NOTE : handle the re-invite
        // Set state to Completed if we are processing a
        // BYE transaction for the dialog.
        // Will be set to TERMINATED after the BYE
        // transaction completes.

        if (sipRequest.getMethod().equals(Request.BYE)) {
            this.setState(COMPLETED_STATE);
        }

        if (firstTransaction == null) {
            // Record the local and remote sequence
            // numbers and the from and to tags for future
            // use on this dialog.
            firstTransaction = transaction;
            // IMPL_NOTE : set the local and remote party
            if (transaction instanceof ServerTransaction) {
                hisTag = sipRequest.getFromHeader().getTag();
                // My tag is assigned when sending response
            } else {
                setLocalSequenceNumber
                        (sipRequest.getCSeqHeader().getSequenceNumber());
                // his tag is known when receiving response
                myTag = sipRequest.getFromHeader().getTag();
                if (myTag == null) {
                    throw new RuntimeException("bad message tag missing!");
                }
            }
        } else {
            String origMethod = 
                transaction.getOriginalRequest().getMethod();
            if (origMethod.equals(Request.ACK)) {
                origMethod = Request.INVITE;
            }
            if (firstTransaction.getOriginalRequest().getMethod().equals
                (origMethod) &&
                (((firstTransaction instanceof ServerTransaction) &&
                (transaction instanceof ClientTransaction)) ||
                ((firstTransaction instanceof ClientTransaction) &&
                (transaction instanceof ServerTransaction)))) {
                // Switch from client side to server side for re-invite
                // (put the other side on hold).

                firstTransaction = transaction;
            }
        }

        if (transaction instanceof ServerTransaction) {
            setRemoteSequenceNumber(sipRequest.getCSeqHeader().
                getSequenceNumber());
        }

        lastTransaction = transaction;

        // set a back ptr in the incoming dialog.
        transaction.setDialog(this);

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "Transaction Added " + this + myTag + "/" + hisTag);
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "TID = " + transaction.getTransactionId() +
                    "/" + transaction.IsServerTransaction());
            // new Exception().printStackTrace();
        }
    
private voidbuildRouteSet(Request request)
Create route set for request.

param
request the input request
throws
SipException if any occurs

        getLastTransaction().buildRouteSet(request);
    
public RequestcreateRequest(java.lang.String method)
Creates a new Request message based on the dialog creating request. This method should be used for but not limited to creating Bye's, Refer's and re-Invite's on the Dialog. The returned Request will be correctly formatted that is it will contain the correct CSeqHeader header, Route headers and requestURI (derived from the remote target). This method should not be used for Ack, that is the application should create the Ack from the MessageFactory. If the route set is not empty, and the first URI in the route set contains the lr parameter (see Section 19.1.1), the UAC MUST place the remote target URI into the Request-URI and MUST include a Route header field containing the route set values in order, including all parameters. If the route set is not empty, and its first URI does not contain the lr parameter, the UAC MUST place the first URI from the route set into the Request-URI, stripping any parameters that are not allowed in a Request-URI. The UAC MUST add a Route header field containing the remainder of the route set values in order, including all parameters. The UAC MUST then place the remote target URI into the Route header field as the last value.

param
method the string value that determines if the request to be created.
return
the newly created Request message on this Dialog.
throws
SipException if the Dialog is not yet established.

        // Set the dialog back pointer.
        if (method == null)
            throw new NullPointerException("null method");
        else if (this.getState() == -1 ||
                ((! method.equals(Request.BYE)) &&
                this.getState() == TERMINATED_STATE)
                || (method.equals(Request.BYE) &&
                this.getState() == EARLY_STATE)) {
            throw new SipException("Dialog not yet established or terminated",
                SipException.DIALOG_UNAVAILABLE);
        }

        Request originalRequest = (Request) getFirstTransaction().getRequest();

        RequestLine requestLine = new RequestLine();
        requestLine.setUri((URI)getRemoteParty().getURI());
        requestLine.setMethod(method);

        Request sipRequest =
                originalRequest.createRequest(requestLine, isServer());

        // Guess of local sequence number - this is being re-set when
        // the request is actually dispatched (reported by Brad Templeton
        // and Antonis Karydas).
        if (! method.equals(Request.ACK)) {
            CSeqHeader cseq = (CSeqHeader) sipRequest.getCSeqHeader();
            cseq.setSequenceNumber(this.localSequenceNumber + 1);
        }

        if (isServer()) {
            // Remove the old via headers.
            sipRequest.removeHeader(Header.VIA);
            // Add a via header for the outbound request based on the
            // transport of the message processor.

            // MessageProcessor messageProcessor = sipStack.getMessageProcessor(
            //         firstTransaction.encapsulatedChannel.getTransport());
            MessageProcessor messageProcessor =
                firstTransaction.encapsulatedChannel.getMessageProcessor();

            ViaHeader via = messageProcessor.getViaHeader();
            sipRequest.addHeader(via);
        }

        FromHeader from = (FromHeader) sipRequest.getFromHeader();
        ToHeader to = (ToHeader) sipRequest.getTo();

        from.setTag(getLocalTag());
        to.setTag(getRemoteTag());

        // RFC 3261, p. 75:
        // A UAC SHOULD include a Contact header field in any target refresh
        // requests within a dialog, and unless there is a need to change it,
        // the URI SHOULD be the same as used in previous requests within the
        // dialog.
        ContactList cl = originalRequest.getContactHeaders();
        if (cl != null) {
            sipRequest.addHeader((ContactList)(cl.clone()));
        }

        // get the route list from the dialog.
        buildRouteSet(sipRequest);

        return sipRequest;
    
public voiddelete()
This method will release all resources associated with this dialog that are tracked by the Provider. Further references to the dialog by incoming messages will result in a mismatch. Since dialog destruction is left reasonably open ended in RFC3261, this delete method is provided for future use and extension methods that do not require a BYE to terminate a dialogue. The basic case of the INVITE and all dialogues that we are aware of today it is expected that BYE requests will end the dialogue.

        // the reaper will get him later.
        setState(TERMINATED_STATE);
    
protected voiddeleteTransactions()
Marks all the transactions in the dialog inactive and ready for garbage collection.

        firstTransaction = null;
        lastTransaction = null;
    
public java.lang.ObjectgetApplicationData()
Gets pointer to opaque application data.

return
application data

        return this.applicationData;
    
public CallIdHeadergetCallId()
Returns the Call-ID for this SipSession. This is the value of the Call-ID header for all messages belonging to this session.

return
the Call-ID for this Dialogue

        Request sipRequest =
                ((Transaction)this.getFirstTransaction()).getOriginalRequest();
        return sipRequest.getCallId();
    
public java.lang.StringgetDialogId()
Gets the id for this dialog.

return
the string identifier for this dialog.

        if (firstTransaction instanceof ServerTransaction) {
            // if (true || dialogId == null) {
            Request sipRequest = (Request)
                ((ServerTransaction) firstTransaction).getOriginalRequest();
            dialogId = sipRequest.getDialogId(true, myTag);
            // }
        } else {
            // This is a client transaction. Compute the dialog id
            // from the tag we have assigned to the outgoing
            // response of the dialog creating transaction.
            if (firstTransaction != null &&
                    ((ClientTransaction)getFirstTransaction()).
                        getLastResponse() != null) {
                dialogId = ((ClientTransaction)getFirstTransaction()).
                        getLastResponse().getDialogId(false, hisTag);
            }
        }

        return dialogId;
    
public TransactiongetFirstTransaction()
Gets the transaction that created this dialog.

return
the first transaction

        return firstTransaction;
    
public RequestgetLastAck()
Gets the last ACK.

return
the last ack

        return this.lastAck;
    
public TransactiongetLastTransaction()
Gets the last transaction from the dialog.

return
last transaction

        return this.lastTransaction;
    
public AddressgetLocalParty()
Gets the local Address for this dialog.

return
the address object of the local party.

        Request sipRequest =
                ((Transaction)this.getFirstTransaction()).getOriginalRequest();
        if (!isServer()) {
            return sipRequest.getFromHeader().getAddress();
        } else {
            return sipRequest.getTo().getAddress();
        }
    
public intgetLocalSequenceNumber()
Gets the local sequence number (for cseq assignment of outgoing requests within this dialog).

return
local sequence number.

        return this.localSequenceNumber;
    
public java.lang.StringgetLocalTag()
Gets local identifier for the dialog. This is used in FromHeader header tag construction for all outgoing client transaction requests for this dialog and for all outgoing responses for this dialog. This is used in ToHeader tag constuction for all outgoing transactions when we are the server of the dialog. Use this when constucting ToHeader header tags for BYE requests when we are the server of the dialog.

return
the local tag.

        return this.myTag;
    
public HopgetNextHop()
Gets the next hop.

return
the next hop
exception
SipException if an error occurs

        // This is already an established dialog so dont consult the router.
        // Route the request based on the request URI.
        RouteList rl = this.getRouteList();
        SipURI sipUri = null;
        String transport;

        if (rl != null && ! rl.isEmpty()) {
            RouteHeader route = (RouteHeader) rl.getFirst();
            sipUri = (SipURI) (route.getAddress().getURI());
            transport = route.getAddress().
                            getParameter(SIPConstants.GENERAL_TRANSPORT);
        } else if (contactRoute != null) {
            sipUri = (SipURI) (contactRoute.getAddress().getURI());
            transport = contactRoute.getAddress().
                            getParameter(SIPConstants.GENERAL_TRANSPORT);
        } else {
            throw new SipException("No route found!",
                SipException.GENERAL_ERROR);
        }

        String host = sipUri.getHost();
        int port = sipUri.getPort();

        if (port == -1) {
            port = SIPConstants.DEFAULT_NONTLS_PORT;
        }

        if (transport == null) {
            transport = SIPConstants.TRANSPORT_UDP;
        }

        return new Hop(host, port, transport);
    
public AddressgetRemoteParty()
Returns the Address identifying the remote party. This is the value of the ToHeader header of locally initiated requests in this dialogue when acting as an User Agent Client.

This is the value of the FromHeader header of recieved responses in this dialogue when acting as an User Agent Server.

return
the address object of the remote party.

        Request sipRequest =
                ((Transaction)this.getFirstTransaction()).getOriginalRequest();
        if (!isServer()) {
            return sipRequest.getTo().getAddress();
        } else {
            return sipRequest.getFromHeader().getAddress();
        }
    
public intgetRemoteSequenceNumber()
Gets the remote sequence number (for cseq assignment of outgoing requests within this dialog).

return
local sequence number.

        return this.remoteSequenceNumber;
    
public java.lang.StringgetRemoteTag()
Gets peer identifier identifier for the dialog. This is used in ToHeader header tag construction for all outgoing requests when we are the client of the dialog. This is used in FromHeader tag construction for all outgoing requests when we are the Server of the dialog. Use this when costructing FromHeader header Tags for BYE requests when we are the server of the dialog.

return
the remote tag (note this is read from a response to an INVITE).

        return hisTag;
    
public AddressgetRemoteTarget()
Returns the Address identifying the remote target. This is the value of the Contact header of recieved Responses for Requests or refresh Requests in this dialogue when acting as an User Agent Client

This is the value of the Contact header of received Requests or refresh Requests in this dialogue when acting as an User

return
the address object of the remote target.

        if (contactRoute == null) {
            return null;
        }
        return contactRoute.getAddress();
    
private RouteListgetRouteList()
Gets the route list.

return
the route list

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

        // Find the top via in the route list.
        Vector li = routeList.getHeaders();
        RouteList retval = new RouteList();

        // If I am a UA then I am not record routing the request.

        retval = new RouteList();

        for (int i = 0; i < li.size(); i++) {
            RouteHeader route = (RouteHeader) li.elementAt(i);
            retval.add(route.clone());
        }

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "----->>> ");
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "getRouteList for " + this);

            if (retval != null) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "RouteList = " + retval.encode());
            }

            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "myRouteList = " + routeList.encode());
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "----->>> ");
        }

        return retval;
    
public java.util.EnumerationgetRouteSet()
Gets the route set for the dialog. When acting as an User Agent Server the route set MUST be set to the list of URIs in the Record-Route header field from the request, taken in order and preserving all URI parameters. When acting as an User Agent Client the route set MUST be set to the list of URIs in the Record-Route header field from the response, taken in reverse order and preserving all URI parameters. If no Record-Route header field is present in the request or response, the route set MUST be set to the empty set. This route set, even if empty, overrides any pre-existing route set for future requests in this dialog.

Requests within a dialog MAY contain Record-Route and Contact header fields. However, these requests do not cause the dialog's route set to be modified.

The User Agent Client uses the remote target and route set to build the Request-URI and Route header field of the request.

return
an Iterator containing a list of route headers to be used for forwarding. Empty iterator is returned if route has not been established.

        if (this.routeList == null)
            return null;
        else
            return this.getRouteList().getElements();
    
public intgetState()
Returns the current state of the dialogue. The states are as follows:
  • Early - A dialog is in the "early" state, which occurs when it is created when a provisional response is recieved to the INVITE Request.
  • Confirmed - A dialog transitions to the "confirmed" state when a 2xx final response is received to the INVITE Request.
  • Completed - A dialog transitions to the "completed" state when a BYE request is sent or received by the User Agent Client.
  • Terminated - A dialog transitions to the "terminated" state when it can be garbage collection.
Independent of the method, if a request outside of a dialog generates a non-2xx final response, any early dialogs created through provisional responses to that request are terminated. If no response arrives at all on the early dialog, it also terminates.

return
a DialogState determining the current state of the dialog.

        return this.dialogState;
    
public intincrementLocalSequenceNumber()
Increments the local CSeqHeader # for the dialog.

return
the incremented local sequence number.

        return ++this.localSequenceNumber;
    
public booleanisAckSeen()
Returns true if the dialog has been acked. The ack is sent up to the TU exactly once when retransmission filter is enabled.

return
true if ACK has been processed

        return this.ackSeen;
    
public booleanisClientDialog()
Returns true if this is a client dialog.

return
true if the transaction that created this dialog is a client transaction and false otherwise.

        Transaction transaction = (Transaction) getFirstTransaction();
        return transaction instanceof ClientTransaction;
    
public booleanisInviteDialog()
Checks if this is an invitation dialog.

return
true if this is an invitation dialog

        return getFirstTransaction().getRequest().getMethod().
                equals(Request.INVITE);
    
public booleanisSecure()
Returns true if this Dialog is secure i.e. if the request arrived over TLS, and the Request-URI contained a SIPS URI, the "secure" flag is set to TRUE.

return
true if this dialogue was established using a sips URI over TLS, and false otherwise.

        return Utils.equalsIgnoreCase(getFirstTransaction().getRequest().
                getRequestURI().getScheme(), SIPConstants.SCHEME_SIPS);
    
public booleanisServer()
Returns true if is server.

return
true if is server transaction created this dialog.

        return getFirstTransaction() instanceof ServerTransaction;
    
public booleanisSubscribeDialog()
Checks if this is a subscription dialog.

return
true if this is a subscription dialog

        String method = getFirstTransaction().getRequest().getMethod();
        // REFER creates an implicit subscription
        return (method.equals(Request.SUBSCRIBE) ||
                method.equals(Request.REFER));
    
private voidprintRouteList()
A debugging print routine.

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "this : " + this);
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "printRouteList : " + this.routeList.encode());

            if (this.contactRoute != null) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "contactRoute : " + this.contactRoute.encode());
            } else {
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "contactRoute : null");
            }
        }
    
public voidprintTags()
Debugging print for the dialog.

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "isServer = " + isServer());
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "localTag = " + getLocalTag());
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "remoteTag = " + getRemoteTag());
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "firstTransaction = " +
                    ((Transaction) firstTransaction).getOriginalRequest());
        }
    
public voidresendAck()
Resends the last ack.

        // Check for null.
        try {
            if (lastAck != null)
                sendAck(lastAck);
        } catch (SipException ex) {
            if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                    "Dialog.resendAck(): SipException occured:" + ex);
                ex.printStackTrace();
            }
        }
    
public voidsendAck(Request request)
Sends ACK Request to the remote party of this Dialogue.

param
request the new ACK Request message to send.
throws
SipException if implementation cannot send the ACK Request for any other reason

        Request ackRequest = (Request) request;

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

        if (isServer()) {
            throw new SipException("Cannot sendAck from " +
                "Server side of Dialog", SipException.DIALOG_UNAVAILABLE);
        }

        if (!ackRequest.getMethod().equals(Request.ACK)) {
            throw new SipException("Bad request method -- should be ACK",
                SipException.INVALID_MESSAGE);
        }

        if (getState() == -1 || getState() == EARLY_STATE) {
            throw new SipException("Bad dialog state " + getState(),
                SipException.INVALID_STATE);
        }

        if (! ((Transaction)getFirstTransaction()).
                getOriginalRequest().getCallId().
                getCallId().equals(((Request)request).
                getCallId().getCallId())) {
            throw new SipException("Bad call ID in request",
                SipException.INVALID_MESSAGE);
        }

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "setting from tag For outgoing ACK = " + this.getLocalTag());
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "setting ToHeader tag for outgoing ACK = " +
                    this.getRemoteTag());
        }

        if (this.getLocalTag() != null)
            ackRequest.getFromHeader().setTag(this.getLocalTag());
        if (this.getRemoteTag() != null)
            ackRequest.getTo().setTag(this.getRemoteTag());

        // Create the route request and set it appropriately.
        // Note that we only need to worry about being on the client
        // side of the request.
        buildRouteSet(ackRequest);

        Hop hop = getNextHop();

        try {
            MessageChannel messageChannel =
                    sipStack.createRawMessageChannel(hop);
            if (messageChannel == null) {
                // procedures of 8.1.2 and 12.2.1.1 of RFC3261 have
                // been tried but the resulting next hop cannot be
                // resolved (recall that the exception thrown is
                // caught and ignored in
                // SIPMessageStack.createMessageChannel() so we end
                // up here with a null messageChannel instead of the
                // exception handler below). All else failing, try
                // the outbound proxy in accordance with 8.1.2, in
                // particular: This ensures that outbound proxies
                // that do not add Record-Route header field values
                // will drop out of the path of subsequent requests.
                // It allows endpoints that cannot resolve the first
                // Route URI to delegate that task to an outbound
                // proxy.
                //
                // if one considers the 'first Route URI' of a
                // request constructed according to 12.2.1.1
                // to be the request URI when the route set is empty.
                Hop outboundProxy = sipStack.getRouter().getOutboundProxy();
                if (outboundProxy == null) {
                    throw new SipException("No route found!",
                        SipException.GENERAL_ERROR);
                }

                messageChannel = sipStack.createRawMessageChannel(
                        outboundProxy);
            }
            // Wrap a client transaction around the raw message channel.
            ClientTransaction clientTransaction =
                    (ClientTransaction)
                    sipStack.createMessageChannel(messageChannel);
            clientTransaction.setOriginalRequest(ackRequest);
            clientTransaction.sendMessage((Message)ackRequest);
            // Do not retransmit the ACK so terminate the transaction
            // immediately.
            this.lastAck = ackRequest;
            clientTransaction.setState(Transaction.TERMINATED_STATE);
        } catch (IOException ex) {
            if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                    "Exception occured: " + ex);
                // ex.printStackTrace();
            }

            throw new SipException("Cold not create message channel",
                SipException.GENERAL_ERROR);
        }
    
public voidsendRequest(ClientTransaction clientTransactionId)
Sends a Request to the remote party of this dialog. This method implies that the application is functioning as UAC hence the underlying SipProvider acts statefully. This method is useful for sending Bye's for terminating a dialog or Re-Invites on the Dialog for third party call control.

This methods will set the FromHeader and the ToHeader tags for the outgoing request and also set the correct sequence number to the outgoing Request and associate the client transaction with this dialog. Note that any tags assigned by the user will be over-written by this method.

The User Agent must not send a BYE on a confirmed INVITE until it has received an ACK for its 2xx response or until the server transaction timeout is received.

When the retransmissionFilter is true, that is the SipProvider takes care of all retransmissions for the application, and the SipProvider can not deliver the Request after multiple retransmits the SipListener will be notified with a {@link TimeoutEvent} when the transaction expires.

param
clientTransactionId the new ClientTransaction object identifying this transaction, this clientTransaction should be requested from SipProvider.getNewClientTransaction
throws
TransactionDoesNotExistException if the serverTransaction does not correspond to any existing server transaction.
throws
SipException if implementation cannot send the Request for any reason.

        if (clientTransactionId == null) {
            throw new NullPointerException("null parameter");
        }

        Request dialogRequest = clientTransactionId.getOriginalRequest();
        String method = dialogRequest.getMethod();
        
        if (method.equals(Request.ACK) || method.equals(Request.CANCEL)) {
            throw new SipException("Bad Request Method: " +
                    dialogRequest.getMethod(), SipException.INVALID_MESSAGE);
        }

        // Cannot send bye until the dialog has been established.
        if (getState() == INITIAL_STATE) {
            throw new SipException("Bad dialog state (-1).",
                SipException.DIALOG_UNAVAILABLE);
        }

        if (Utils.equalsIgnoreCase(dialogRequest.getMethod(), Request.BYE) &&
                getState() == EARLY_STATE) {
            throw new SipException("Bad dialog state ",
                SipException.DIALOG_UNAVAILABLE);
        }

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "dialog.sendRequest " +
                " dialog = " + this + "\ndialogRequest = \n" +
                dialogRequest);
        }

        if (dialogRequest.getTopmostVia() == null) {
            ViaHeader via = ((ClientTransaction) clientTransactionId).
                getOutgoingViaHeader();
            dialogRequest.addHeader(via);
        }

        if (! ((Transaction)this.getFirstTransaction()).
                getOriginalRequest().getCallId().
                getCallId().equals(dialogRequest.
                getCallId().getCallId())) {
            throw new SipException("Bad call ID in request",
                SipException.INVALID_MESSAGE);
        }

        // Set the dialog back pointer.
        ((ClientTransaction)clientTransactionId).setDialog(this);

        FromHeader from = (FromHeader) dialogRequest.getFromHeader();
        ToHeader to = (ToHeader) dialogRequest.getTo();

        from.setTag(this.getLocalTag());
        to.setTag(this.getRemoteTag());

        // Caller has not assigned the route header - set the route header
        // and the request URI for the outgoing request.
        // Bugs reported by Brad Templeton.

        buildRouteSet(dialogRequest);

        Hop hop = this.getNextHop();

        try {
            MessageChannel messageChannel =
                    sipStack.createRawMessageChannel(hop);
            ((ClientTransaction) clientTransactionId).
                    encapsulatedChannel = messageChannel;

            if (messageChannel == null) {
                // procedures of 8.1.2 and 12.2.1.1 of RFC3261 have
                // been tried but the resulting next hop cannot be
                // resolved (recall that the exception thrown is
                // caught and ignored in
                // SIPMessageStack.createMessageChannel() so we end
                // up here with a null messageChannel instead of the
                // exception handler below). All else failing, try
                // the outbound proxy in accordance with 8.1.2, in
                // particular: This ensures that outbound proxies
                // that do not add Record-Route header field values
                // will drop out of the path of subsequent requests.
                // It allows endpoints that cannot resolve the first
                // Route URI to delegate that task to an outbound
                // proxy.
                //
                // if one considers the 'first Route URI' of a
                // request constructed according to 12.2.1.1
                // to be the request URI when the route set is empty.
                Hop outboundProxy = sipStack.getRouter().getOutboundProxy();
                if (outboundProxy == null) {
                    throw new SipException("No route found!",
                        SipException.GENERAL_ERROR);
                }
                messageChannel =
                    sipStack.createRawMessageChannel(outboundProxy);
            }

            ((ClientTransaction) clientTransactionId).encapsulatedChannel =
                messageChannel;
        } catch (IOException ex) {
            if (Logging.REPORT_LEVEL <= Logging.ERROR) {
                Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
                    "Exception occured: " + ex);
            }

            throw new SipException("Could not create message channel.",
                SipException.GENERAL_ERROR);
        }

        // Increment before setting!!
        localSequenceNumber ++;
        dialogRequest.getCSeqHeader().
                setSequenceNumber(getLocalSequenceNumber());

        if (this.isServer()) {
            // ServerTransaction serverTransaction = (ServerTransaction)
            //     getFirstTransaction();

            from.setTag(this.myTag);
            to.setTag(this.hisTag);

            try {
                ((ClientTransaction) clientTransactionId).sendMessage(
                        dialogRequest);
                // If the method is BYE then mark the dialog completed.
                if (dialogRequest.getMethod().equals(Request.BYE)) {
                    setState(COMPLETED_STATE);
                }
            } catch (IOException ex) {
                throw new SipException("Error sending message.",
                    SipException.GENERAL_ERROR);
            }
        } else {
            // I am the client so I do not swap headers.
            // ClientTransaction clientTransaction = (ClientTransaction)
            //     getFirstTransaction();

            if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "setting tags from " + this.getDialogId());
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "fromTag " + this.myTag);
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "toTag " + this.hisTag);
            }

            from.setTag(this.myTag);
            to.setTag(this.hisTag);

            try {
                ((ClientTransaction)clientTransactionId).
                        sendMessage(dialogRequest);

                // If the method is BYE then mark the dialog completed.
                if (dialogRequest.getMethod().equals(Request.BYE)) {
                    this.setState(COMPLETED_STATE);
                }
            } catch (IOException ex) {
                throw new SipException("Error sending message.",
                    SipException.GENERAL_ERROR);
            }
        }
    
public voidsetApplicationData(java.lang.Object applicationData)
Sets the pointer to application data.

param
applicationData the new application data


                     
        
        this.applicationData = applicationData;
    
public voidsetDefaultRoute(RouteHeader defaultRoute)
Sets the default route (the default next hop for the proxy or the proxy address for the user agent).

param
defaultRoute is the default route to set.

        this.defaultRoute = (RouteHeader) defaultRoute.clone();
        // addRoute(defaultRoute,false);
    
public voidsetDialogId(java.lang.String newDialogId)
Sets the dialog identifier.

param
newDialogId the new dialog identifier

        dialogId = newDialogId;
    
protected voidsetLocalSequenceNumber(int lCseq)
Sets the local sequece number for the dialog (defaults to 1 when the dialog is created).

param
lCseq is the local cseq number.

        this.localSequenceNumber = lCseq;
    
public voidsetLocalTag(java.lang.String mytag)
Sets local tag for the transaction.

param
mytag is the tag to use in FromHeader headers client transactions that belong to this dialog and for generating ToHeader tags for Server transaction requests that belong to this dialog.

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "set Local tag " + mytag + " " + this.dialogId);
            // new Exception().printStackTrace();
        }

        myTag = mytag;
    
protected voidsetRemoteSequenceNumber(int rCseq)
Sets the remote sequence number for the dialog.

param
rCseq is the remote cseq number.

        this.remoteSequenceNumber = rCseq;
    
public voidsetRemoteTag(java.lang.String hisTag)
Sets the remote tag.

param
hisTag is the remote tag to set.

        this.hisTag = hisTag;
    
protected voidsetRetransmissionTicks()
Sets retransmission ticks.

        retransmissionTicksLeft = 1;
        prevRetransmissionTicks = 1;
    
private voidsetRouteTransport(RouteHeader route, java.lang.String transport)
Sets the transport parameter to route header.

param
route the route header for setting transport param
param
transport the name of transport param

        if (route.getAddress().
            getParameter(SIPConstants.GENERAL_TRANSPORT) == null) {
            route.getAddress().setParameter(SIPConstants.GENERAL_TRANSPORT,
                                            transport);
        }
    
public voidsetStack(SIPTransactionStack sipStack)
Sets the stack address. Prevent us from routing messages to ourselves.

param
sipStack the address of the SIP stack.

        this.sipStack = sipStack;
    
public voidsetState(int state)
Sets the state for this dialog.

param
state is the state to set for the dialog.

        if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
            Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                "Setting dialog state for " + this);
            // new Exception().printStackTrace();

            if (state != INITIAL_STATE &&
                    state != this.dialogState) {
                Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
                    "New dialog state is " +
                    state +
                    "dialogId = " +
                    this.getDialogId());
            }

        }

        this.dialogState = state;
    
public voidsetUser(java.lang.String user)
Sets the user name for the default route.

param
user is the user name to set for the default route.

        this.user = user;
    
protected booleantoRetransmitFinalResponse()
Returns yes if the last response is to be retransmitted.

return
true if final response retransmitted

        if (--retransmissionTicksLeft == 0) {
            retransmissionTicksLeft = 2*prevRetransmissionTicks;
            prevRetransmissionTicks = retransmissionTicksLeft;
            return true;
        } else {
            return false;
        }