0001: /*
0002: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights
0003: * Reserved. Use is subject to license terms.
0004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0005: *
0006: * This program is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU General Public License version
0008: * 2 only, as published by the Free Software Foundation.
0009: *
0010: * This program is distributed in the hope that it will be useful, but
0011: * WITHOUT ANY WARRANTY; without even the implied warranty of
0012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0013: * General Public License version 2 for more details (a copy is
0014: * included at /legal/license.txt).
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * version 2 along with this work; if not, write to the Free Software
0018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0019: * 02110-1301 USA
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0022: * Clara, CA 95054 or visit www.sun.com if you need additional
0023: * information or have any questions.
0024: */
0025: /*
0026: */
0027: package gov.nist.siplite.stack;
0028:
0029: import java.util.*;
0030: import gov.nist.siplite.header.*;
0031: import gov.nist.siplite.address.*;
0032: import gov.nist.siplite.message.*;
0033: import gov.nist.siplite.*;
0034: import gov.nist.core.*;
0035: import java.io.IOException;
0036: import javax.microedition.sip.SipException;
0037:
0038: import com.sun.midp.log.Logging;
0039: import com.sun.midp.log.LogChannels;
0040:
0041: /**
0042: * Tracks dialogs. A dialog is a peer to peer association of communicating
0043: * SIP entities. For INVITE transactions, a Dialog is created when a success
0044: * message is received (i.e. a response that has a ToHeader tag).
0045: * The SIP Protocol stores enough state in the
0046: * message structure to extract a dialog identifier that can be used to
0047: * retrieve this structure from the SipStack.
0048: *
0049: * @version JAIN-SIP-1.1
0050: *
0051: * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
0052: *
0053: */
0054: public class Dialog {
0055: /** Opaque pointer to application data. */
0056: private Object applicationData;
0057: /** First transaction. */
0058: private Transaction firstTransaction;
0059: /** Last transaction. */
0060: private Transaction lastTransaction;
0061: /** Dialog identifier. */
0062: private String dialogId;
0063: /** Local sequence number. */
0064: private int localSequenceNumber;
0065: /** Remote sequence number. */
0066: private int remoteSequenceNumber;
0067: /** Local tag. */
0068: private String myTag;
0069: /** Remote tag. */
0070: private String hisTag;
0071: /** Route list. */
0072: private RouteList routeList;
0073: /** Contact route. */
0074: private RouteHeader contactRoute;
0075: /** User name. */
0076: private String user;
0077: /** Default route. */
0078: private RouteHeader defaultRoute;
0079: /** Current context SIP stack. */
0080: private SIPTransactionStack sipStack;
0081: /** Current Dialog state. */
0082: private int dialogState;
0083: /** ACK has been processed. */
0084: protected boolean ackSeen;
0085: /** Previous ACk. */
0086: protected Request lastAck;
0087: /** List of active subscriptions. */
0088: public SubscriptionList subscriptionList;
0089: /** Time remaining for retransmit. */
0090: private int retransmissionTicksLeft;
0091: /** Time used on previous retransmit. */
0092: private int prevRetransmissionTicks;
0093: /** Initial state. */
0094: public final static int INITIAL_STATE = -1;
0095: /** Early state. */
0096: public final static int EARLY_STATE = 1;
0097: /** Confirmed state. */
0098: public final static int CONFIRMED_STATE = 2;
0099: /** Completed state. */
0100: public final static int COMPLETED_STATE = 3;
0101: /** Terminated state. */
0102: public final static int TERMINATED_STATE = 4;
0103:
0104: /**
0105: * Sets the pointer to application data.
0106: * @param applicationData the new application data
0107: */
0108: public void setApplicationData(Object applicationData) {
0109: this .applicationData = applicationData;
0110: }
0111:
0112: /**
0113: * Gets pointer to opaque application data.
0114: * @return application data
0115: */
0116: public Object getApplicationData() {
0117: return this .applicationData;
0118: }
0119:
0120: /**
0121: * A debugging print routine.
0122: */
0123: private void printRouteList() {
0124: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0125: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0126: "this : " + this );
0127: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0128: "printRouteList : " + this .routeList.encode());
0129:
0130: if (this .contactRoute != null) {
0131: Logging.report(Logging.INFORMATION,
0132: LogChannels.LC_JSR180, "contactRoute : "
0133: + this .contactRoute.encode());
0134: } else {
0135: Logging.report(Logging.INFORMATION,
0136: LogChannels.LC_JSR180, "contactRoute : null");
0137: }
0138: }
0139: }
0140:
0141: /**
0142: * Gets the next hop.
0143: * @return the next hop
0144: * @exception SipException if an error occurs
0145: */
0146: public Hop getNextHop() throws SipException {
0147: // This is already an established dialog so dont consult the router.
0148: // Route the request based on the request URI.
0149: RouteList rl = this .getRouteList();
0150: SipURI sipUri = null;
0151: String transport;
0152:
0153: if (rl != null && !rl.isEmpty()) {
0154: RouteHeader route = (RouteHeader) rl.getFirst();
0155: sipUri = (SipURI) (route.getAddress().getURI());
0156: transport = route.getAddress().getParameter(
0157: SIPConstants.GENERAL_TRANSPORT);
0158: } else if (contactRoute != null) {
0159: sipUri = (SipURI) (contactRoute.getAddress().getURI());
0160: transport = contactRoute.getAddress().getParameter(
0161: SIPConstants.GENERAL_TRANSPORT);
0162: } else {
0163: throw new SipException("No route found!",
0164: SipException.GENERAL_ERROR);
0165: }
0166:
0167: String host = sipUri.getHost();
0168: int port = sipUri.getPort();
0169:
0170: if (port == -1) {
0171: port = SIPConstants.DEFAULT_NONTLS_PORT;
0172: }
0173:
0174: if (transport == null) {
0175: transport = SIPConstants.TRANSPORT_UDP;
0176: }
0177:
0178: return new Hop(host, port, transport);
0179: }
0180:
0181: /**
0182: * Returns true if this is a client dialog.
0183: *
0184: * @return true if the transaction that created this dialog is a
0185: *client transaction and false otherwise.
0186: */
0187: public boolean isClientDialog() {
0188: Transaction transaction = (Transaction) getFirstTransaction();
0189: return transaction instanceof ClientTransaction;
0190: }
0191:
0192: /**
0193: * Sets the state for this dialog.
0194: *
0195: * @param state is the state to set for the dialog.
0196: */
0197: public void setState(int state) {
0198: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0199: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0200: "Setting dialog state for " + this );
0201: // new Exception().printStackTrace();
0202:
0203: if (state != INITIAL_STATE && state != this .dialogState) {
0204: Logging.report(Logging.INFORMATION,
0205: LogChannels.LC_JSR180, "New dialog state is "
0206: + state + "dialogId = "
0207: + this .getDialogId());
0208: }
0209:
0210: }
0211:
0212: this .dialogState = state;
0213: }
0214:
0215: /**
0216: * Debugging print for the dialog.
0217: */
0218: public void printTags() {
0219: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0220: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0221: "isServer = " + isServer());
0222: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0223: "localTag = " + getLocalTag());
0224: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0225: "remoteTag = " + getRemoteTag());
0226: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0227: "firstTransaction = "
0228: + ((Transaction) firstTransaction)
0229: .getOriginalRequest());
0230: }
0231: }
0232:
0233: /**
0234: * Marks that the dialog has seen an ACK.
0235: * @param sipRequest the current SIP transaction
0236: */
0237: public void ackReceived(Request sipRequest) {
0238: if (isServer()) {
0239: ServerTransaction st = (ServerTransaction) getFirstTransaction();
0240: if (st == null) {
0241: return;
0242: }
0243:
0244: // Suppress retransmission of the final response (in case
0245: // retransmission filter is being used).
0246: if (st.getOriginalRequest().getCSeqHeader()
0247: .getSequenceNumber() == sipRequest.getCSeqHeader()
0248: .getSequenceNumber()) {
0249: st.setState(Transaction.TERMINATED_STATE);
0250: ackSeen = true;
0251: lastAck = sipRequest;
0252: }
0253: }
0254: }
0255:
0256: /**
0257: * Returns true if the dialog has been acked. The ack is sent up to the
0258: * TU exactly once when retransmission filter is enabled.
0259: * @return true if ACK has been processed
0260: */
0261: public boolean isAckSeen() {
0262: return this .ackSeen;
0263: }
0264:
0265: /**
0266: * Gets the last ACK.
0267: * @return the last ack
0268: */
0269: public Request getLastAck() {
0270: return this .lastAck;
0271: }
0272:
0273: /**
0274: * Gets the transaction that created this dialog.
0275: * @return the first transaction
0276: */
0277: public Transaction getFirstTransaction() {
0278: return firstTransaction;
0279: }
0280:
0281: /**
0282: * Gets the route set for the dialog.
0283: * When acting as an User Agent Server
0284: * the route set MUST be set to the list of URIs in the
0285: * Record-Route header field from the request, taken in order and
0286: * preserving all URI parameters. When acting as an User Agent
0287: * Client the route set MUST be set to the list of URIs in the
0288: * Record-Route header field from the response, taken in
0289: * reverse order and preserving all URI parameters. If no Record-Route
0290: * header field is present in the request or response,
0291: * the route set MUST be set to the empty set. This route set,
0292: * even if empty, overrides any
0293: * pre-existing route set for future requests in this dialog.
0294: * <p>
0295: * Requests within a dialog MAY contain Record-Route
0296: * and Contact header fields.
0297: * However, these requests do not cause the dialog's route set to be
0298: * modified.
0299: * <p>
0300: * The User Agent Client uses the remote target
0301: * and route set to build the
0302: * Request-URI and Route header field of the request.
0303: *
0304: * @return an Iterator containing a list of route headers to be used for
0305: * forwarding. Empty iterator is returned if route has not
0306: * been established.
0307: */
0308: public Enumeration getRouteSet() {
0309: if (this .routeList == null)
0310: return null;
0311: else
0312: return this .getRouteList().getElements();
0313: }
0314:
0315: /**
0316: * Gets the route list.
0317: * @return the route list
0318: */
0319: private RouteList getRouteList() {
0320: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0321: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0322: "getRouteList " + this .getDialogId());
0323: }
0324:
0325: // Find the top via in the route list.
0326: Vector li = routeList.getHeaders();
0327: RouteList retval = new RouteList();
0328:
0329: // If I am a UA then I am not record routing the request.
0330:
0331: retval = new RouteList();
0332:
0333: for (int i = 0; i < li.size(); i++) {
0334: RouteHeader route = (RouteHeader) li.elementAt(i);
0335: retval.add(route.clone());
0336: }
0337:
0338: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0339: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0340: "----->>> ");
0341: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0342: "getRouteList for " + this );
0343:
0344: if (retval != null) {
0345: Logging.report(Logging.INFORMATION,
0346: LogChannels.LC_JSR180, "RouteList = "
0347: + retval.encode());
0348: }
0349:
0350: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0351: "myRouteList = " + routeList.encode());
0352: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0353: "----->>> ");
0354: }
0355:
0356: return retval;
0357: }
0358:
0359: /**
0360: * Sets the stack address.
0361: * Prevent us from routing messages to ourselves.
0362: * @param sipStack the address of the SIP stack.
0363: */
0364: public void setStack(SIPTransactionStack sipStack) {
0365: this .sipStack = sipStack;
0366: }
0367:
0368: /**
0369: * Sets the default route (the default next hop for the proxy or
0370: * the proxy address for the user agent).
0371: *
0372: * @param defaultRoute is the default route to set.
0373: *
0374: */
0375: public void setDefaultRoute(RouteHeader defaultRoute) {
0376: this .defaultRoute = (RouteHeader) defaultRoute.clone();
0377: // addRoute(defaultRoute,false);
0378: }
0379:
0380: /**
0381: * Sets the user name for the default route.
0382: *
0383: * @param user is the user name to set for the default route.
0384: *
0385: */
0386: public void setUser(String user) {
0387: this .user = user;
0388: }
0389:
0390: /**
0391: * Adds a route list extracted from a record route list.
0392: * If this is a server dialog then we assume that the record
0393: * are added to the route list IN order. If this is a client
0394: * dialog then we assume that the record route headers give us
0395: * the route list to add in reverse order.
0396: *
0397: * @param recordRouteList -- the record route list from the incoming
0398: * @param transport -- the transport parameter
0399: * message.
0400: */
0401: private void addRoute(RecordRouteList recordRouteList,
0402: String transport) {
0403: if (this .isClientDialog()) {
0404: // This is a client dialog so we extract the record
0405: // route from the response and reverse its order to
0406: // careate a route list.
0407: this .routeList = new RouteList();
0408: // start at the end of the list and walk backwards
0409: Vector li = recordRouteList.getHeaders();
0410: for (int i = li.size() - 1; i >= 0; i--) {
0411: RecordRouteHeader rr = (RecordRouteHeader) li
0412: .elementAt(i);
0413: Address addr = (Address) rr.getAddress();
0414: RouteHeader route = new RouteHeader();
0415: route.setAddress((Address) (rr.getAddress()).clone());
0416: route.setParameters((NameValueList) rr.getParameters()
0417: .clone());
0418: setRouteTransport(route, transport);
0419: this .routeList.add(route);
0420: }
0421: } else {
0422: // This is a server dialog. The top most record route
0423: // header is the one that is closest to us. We extract the
0424: // route list in the same order as the addresses in the
0425: // incoming request.
0426: this .routeList = new RouteList();
0427: Vector li = recordRouteList.getHeaders();
0428: for (int i = 0; i < li.size(); i++) {
0429: RecordRouteHeader rr = (RecordRouteHeader) li
0430: .elementAt(i);
0431: RouteHeader route = new RouteHeader();
0432: route.setAddress((Address) (rr.getAddress()).clone());
0433: route.setParameters((NameValueList) rr.getParameters()
0434: .clone());
0435: setRouteTransport(route, transport);
0436: routeList.add(route);
0437: }
0438: }
0439: }
0440:
0441: /**
0442: * Sets the transport parameter to route header.
0443: *
0444: * @param route the route header for setting transport param
0445: * @param transport the name of transport param
0446: *
0447: */
0448: private void setRouteTransport(RouteHeader route, String transport) {
0449: if (route.getAddress().getParameter(
0450: SIPConstants.GENERAL_TRANSPORT) == null) {
0451: route.getAddress().setParameter(
0452: SIPConstants.GENERAL_TRANSPORT, transport);
0453: }
0454: }
0455:
0456: /**
0457: * Adds a route list extacted from the contact list of the incoming
0458: * message.
0459: *
0460: * @param contactList contact list extracted from the incoming
0461: * message.
0462: * @param transport the transport parameter
0463: *
0464: */
0465: private void addRoute(ContactList contactList, String transport) {
0466: if (contactList.size() == 0)
0467: return;
0468: ContactHeader contact = (ContactHeader) contactList.getFirst();
0469: RouteHeader route = new RouteHeader();
0470: route.setAddress((Address) ((Address) (contact.getAddress()))
0471: .clone());
0472: setRouteTransport(route, transport);
0473: this .contactRoute = route;
0474: }
0475:
0476: /**
0477: * Extracts the route information from this SIP Message and
0478: * add the relevant information to the route set.
0479: * @param sipMessage is the SIP message for which we want
0480: * to add the route.
0481: */
0482: public synchronized void addRoute(Message sipMessage) {
0483: String method = sipMessage.getCSeqHeader().getMethod();
0484:
0485: // cannot add route list after the dialog is initialized.
0486: try {
0487: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0488: Logging.report(Logging.INFORMATION,
0489: LogChannels.LC_JSR180,
0490: "addRoute: dialogState: " + this + "state = "
0491: + this .getState());
0492: }
0493:
0494: String transport = ((ViaHeader) (sipMessage.getViaHeaders()
0495: .getFirst())).getTransport();
0496:
0497: // UPDATE processing: change route according to contact list
0498: if (Request.UPDATE.equals(method)
0499: && ((this .dialogState == EARLY_STATE) || (this .dialogState == CONFIRMED_STATE))) {
0500:
0501: ContactList contactList = sipMessage
0502: .getContactHeaders();
0503: if (contactList != null) {
0504: this .addRoute(contactList, transport);
0505: }
0506: return;
0507: }
0508:
0509: if (this .dialogState == CONFIRMED_STATE
0510: || this .dialogState == COMPLETED_STATE
0511: || this .dialogState == TERMINATED_STATE) {
0512: return;
0513: }
0514:
0515: if (!isServer()) {
0516: // I am CLIENT dialog.
0517: if (sipMessage instanceof Response) {
0518: Response sipResponse = (Response) sipMessage;
0519: if (sipResponse.getStatusCode() == 100) {
0520: // Do nothing for trying messages.
0521: return;
0522: }
0523:
0524: RecordRouteList rrlist = sipMessage
0525: .getRecordRouteHeaders();
0526:
0527: // Add the route set from the incoming response in reverse
0528: // order
0529: if (rrlist != null) {
0530: this .addRoute(rrlist, transport);
0531: } else {
0532: // Set the rotue list to the last seen route list.
0533: this .routeList = new RouteList();
0534: }
0535:
0536: ContactList contactList = sipMessage
0537: .getContactHeaders();
0538: if (contactList != null) {
0539: if (sipResponse.getStatusCode() / 100 == 2
0540: && (dialogState == INITIAL_STATE || dialogState == EARLY_STATE)) {
0541: ContactHeader contact = (ContactHeader) contactList
0542: .getFirst();
0543:
0544: if (contact != null) {
0545: String newTransport = contact
0546: .getAddress()
0547: .getParameter(
0548: SIPConstants.GENERAL_TRANSPORT);
0549: if (newTransport != null) {
0550: transport = newTransport;
0551: }
0552: }
0553: }
0554:
0555: this .addRoute(contactList, transport);
0556: }
0557: }
0558: } else {
0559: if (sipMessage instanceof Request) {
0560: // Incoming Request has the route list
0561: RecordRouteList rrlist = sipMessage
0562: .getRecordRouteHeaders();
0563: // Add the route set from the incoming response in reverse
0564: // order
0565: if (rrlist != null) {
0566:
0567: this .addRoute(rrlist, transport);
0568: } else {
0569: // Set the rotue list to the last seen route list.
0570: this .routeList = new RouteList();
0571: }
0572: // put the contact header from the incoming request into
0573: // the route set.
0574: ContactList contactList = sipMessage
0575: .getContactHeaders();
0576: if (contactList != null) {
0577: this .addRoute(contactList, transport);
0578: }
0579: }
0580: }
0581: } finally {
0582: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0583: // new Exception()printStackTrace();
0584: Logging.report(Logging.INFORMATION,
0585: LogChannels.LC_JSR180, "added a route = "
0586: + routeList.encode()
0587: + "contactRoute = " + contactRoute);
0588: }
0589: }
0590: }
0591:
0592: /**
0593: * Protected Dialog constructor.
0594: */
0595: private Dialog() {
0596: subscriptionList = new SubscriptionList();
0597: routeList = new RouteList();
0598: dialogState = -1; // not yet initialized.
0599: localSequenceNumber = 0;
0600: remoteSequenceNumber = -1;
0601: }
0602:
0603: /**
0604: * Sets the dialog identifier.
0605: * @param newDialogId the new dialog identifier
0606: */
0607: public void setDialogId(String newDialogId) {
0608: dialogId = newDialogId;
0609: }
0610:
0611: /**
0612: * Constructor given the first transaction.
0613: *
0614: * @param transaction is the first transaction.
0615: */
0616: protected Dialog(Transaction transaction) {
0617: this ();
0618: addTransaction(transaction);
0619: }
0620:
0621: /**
0622: * Create route set for request.
0623: *
0624: * @param request the input request
0625: * @throws SipException if any occurs
0626: */
0627: private void buildRouteSet(Request request) throws SipException {
0628: getLastTransaction().buildRouteSet(request);
0629: }
0630:
0631: /**
0632: * Returns true if is server.
0633: *
0634: * @return true if is server transaction created this dialog.
0635: */
0636: public boolean isServer() {
0637: return getFirstTransaction() instanceof ServerTransaction;
0638: }
0639:
0640: /**
0641: * Gets the id for this dialog.
0642: *
0643: * @return the string identifier for this dialog.
0644: */
0645: public String getDialogId() {
0646: if (firstTransaction instanceof ServerTransaction) {
0647: // if (true || dialogId == null) {
0648: Request sipRequest = (Request) ((ServerTransaction) firstTransaction)
0649: .getOriginalRequest();
0650: dialogId = sipRequest.getDialogId(true, myTag);
0651: // }
0652: } else {
0653: // This is a client transaction. Compute the dialog id
0654: // from the tag we have assigned to the outgoing
0655: // response of the dialog creating transaction.
0656: if (firstTransaction != null
0657: && ((ClientTransaction) getFirstTransaction())
0658: .getLastResponse() != null) {
0659: dialogId = ((ClientTransaction) getFirstTransaction())
0660: .getLastResponse().getDialogId(false, hisTag);
0661: }
0662: }
0663:
0664: return dialogId;
0665: }
0666:
0667: /**
0668: * Adds a transaction record to the dialog.
0669: *
0670: * @param transaction is the transaction to add to the dialog.
0671: */
0672: public void addTransaction(Transaction transaction) {
0673: Request sipRequest = (Request) transaction.getOriginalRequest();
0674:
0675: // Processing a re-invite.
0676: // IMPL_NOTE : handle the re-invite
0677: // Set state to Completed if we are processing a
0678: // BYE transaction for the dialog.
0679: // Will be set to TERMINATED after the BYE
0680: // transaction completes.
0681:
0682: if (sipRequest.getMethod().equals(Request.BYE)) {
0683: this .setState(COMPLETED_STATE);
0684: }
0685:
0686: if (firstTransaction == null) {
0687: // Record the local and remote sequence
0688: // numbers and the from and to tags for future
0689: // use on this dialog.
0690: firstTransaction = transaction;
0691: // IMPL_NOTE : set the local and remote party
0692: if (transaction instanceof ServerTransaction) {
0693: hisTag = sipRequest.getFromHeader().getTag();
0694: // My tag is assigned when sending response
0695: } else {
0696: setLocalSequenceNumber(sipRequest.getCSeqHeader()
0697: .getSequenceNumber());
0698: // his tag is known when receiving response
0699: myTag = sipRequest.getFromHeader().getTag();
0700: if (myTag == null) {
0701: throw new RuntimeException(
0702: "bad message tag missing!");
0703: }
0704: }
0705: } else {
0706: String origMethod = transaction.getOriginalRequest()
0707: .getMethod();
0708: if (origMethod.equals(Request.ACK)) {
0709: origMethod = Request.INVITE;
0710: }
0711: if (firstTransaction.getOriginalRequest().getMethod()
0712: .equals(origMethod)
0713: && (((firstTransaction instanceof ServerTransaction) && (transaction instanceof ClientTransaction)) || ((firstTransaction instanceof ClientTransaction) && (transaction instanceof ServerTransaction)))) {
0714: // Switch from client side to server side for re-invite
0715: // (put the other side on hold).
0716:
0717: firstTransaction = transaction;
0718: }
0719: }
0720:
0721: if (transaction instanceof ServerTransaction) {
0722: setRemoteSequenceNumber(sipRequest.getCSeqHeader()
0723: .getSequenceNumber());
0724: }
0725:
0726: lastTransaction = transaction;
0727:
0728: // set a back ptr in the incoming dialog.
0729: transaction.setDialog(this );
0730:
0731: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0732: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0733: "Transaction Added " + this + myTag + "/" + hisTag);
0734: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0735: "TID = " + transaction.getTransactionId() + "/"
0736: + transaction.IsServerTransaction());
0737: // new Exception().printStackTrace();
0738: }
0739: }
0740:
0741: /**
0742: * Sets the remote tag.
0743: *
0744: * @param hisTag is the remote tag to set.
0745: */
0746: public void setRemoteTag(String hisTag) {
0747: this .hisTag = hisTag;
0748: }
0749:
0750: /**
0751: * Gets the last transaction from the dialog.
0752: * @return last transaction
0753: */
0754: public Transaction getLastTransaction() {
0755: return this .lastTransaction;
0756: }
0757:
0758: /**
0759: * Sets the local sequece number for the dialog (defaults to 1 when
0760: * the dialog is created).
0761: *
0762: * @param lCseq is the local cseq number.
0763: *
0764: */
0765: protected void setLocalSequenceNumber(int lCseq) {
0766: this .localSequenceNumber = lCseq;
0767: }
0768:
0769: /**
0770: * Sets the remote sequence number for the dialog.
0771: *
0772: * @param rCseq is the remote cseq number.
0773: *
0774: */
0775: protected void setRemoteSequenceNumber(int rCseq) {
0776: this .remoteSequenceNumber = rCseq;
0777: }
0778:
0779: /**
0780: * Increments the local CSeqHeader # for the dialog.
0781: *
0782: * @return the incremented local sequence number.
0783: *
0784: */
0785: public int incrementLocalSequenceNumber() {
0786: return ++this .localSequenceNumber;
0787: }
0788:
0789: /**
0790: * Gets the remote sequence number (for cseq assignment of outgoing
0791: * requests within this dialog).
0792: *
0793: * @return local sequence number.
0794: */
0795: public int getRemoteSequenceNumber() {
0796: return this .remoteSequenceNumber;
0797: }
0798:
0799: /**
0800: * Gets the local sequence number (for cseq assignment of outgoing
0801: * requests within this dialog).
0802: *
0803: * @return local sequence number.
0804: */
0805: public int getLocalSequenceNumber() {
0806: return this .localSequenceNumber;
0807: }
0808:
0809: /**
0810: * Gets local identifier for the dialog.
0811: * This is used in FromHeader header tag construction
0812: * for all outgoing client transaction requests for
0813: * this dialog and for all outgoing responses for this dialog.
0814: * This is used in ToHeader tag constuction for all outgoing
0815: * transactions when we are the server of the dialog.
0816: * Use this when constucting ToHeader header tags for BYE requests
0817: * when we are the server of the dialog.
0818: *
0819: * @return the local tag.
0820: */
0821: public String getLocalTag() {
0822: return this .myTag;
0823: }
0824:
0825: /**
0826: * Gets peer identifier identifier for the dialog.
0827: * This is used in ToHeader header tag construction for all outgoing
0828: * requests when we are the client of the dialog.
0829: * This is used in FromHeader tag construction for all outgoing
0830: * requests when we are the Server of the dialog. Use
0831: * this when costructing FromHeader header Tags for BYE requests
0832: * when we are the server of the dialog.
0833: *
0834: * @return the remote tag
0835: * (note this is read from a response to an INVITE).
0836: *
0837: */
0838: public String getRemoteTag() {
0839: return hisTag;
0840: }
0841:
0842: /**
0843: * Sets local tag for the transaction.
0844: *
0845: * @param mytag is the tag to use in FromHeader headers client
0846: * transactions that belong to this dialog and for
0847: * generating ToHeader tags for Server transaction requests that belong
0848: * to this dialog.
0849: */
0850: public void setLocalTag(String mytag) {
0851: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0852: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0853: "set Local tag " + mytag + " " + this .dialogId);
0854: // new Exception().printStackTrace();
0855: }
0856:
0857: myTag = mytag;
0858: }
0859:
0860: /**
0861: * Marks all the transactions in the dialog inactive and ready
0862: * for garbage collection.
0863: */
0864: protected void deleteTransactions() {
0865: firstTransaction = null;
0866: lastTransaction = null;
0867: }
0868:
0869: /**
0870: * This method will release all resources associated with this dialog
0871: * that are tracked by the Provider. Further references to the dialog by
0872: * incoming messages will result in a mismatch.
0873: * Since dialog destruction is left reasonably open ended in RFC3261,
0874: * this delete method is provided
0875: * for future use and extension methods that do not require a BYE to
0876: * terminate a dialogue. The basic case of the INVITE and all dialogues
0877: * that we are aware of today it is expected that BYE requests will
0878: * end the dialogue.
0879: */
0880: public void delete() {
0881: // the reaper will get him later.
0882: setState(TERMINATED_STATE);
0883: }
0884:
0885: /**
0886: * Returns the Call-ID for this SipSession. This is the value of the
0887: * Call-ID header for all messages belonging to this session.
0888: *
0889: * @return the Call-ID for this Dialogue
0890: */
0891: public CallIdHeader getCallId() {
0892: Request sipRequest = ((Transaction) this .getFirstTransaction())
0893: .getOriginalRequest();
0894: return sipRequest.getCallId();
0895: }
0896:
0897: /**
0898: * Gets the local Address for this dialog.
0899: *
0900: * @return the address object of the local party.
0901: */
0902: public Address getLocalParty() {
0903: Request sipRequest = ((Transaction) this .getFirstTransaction())
0904: .getOriginalRequest();
0905: if (!isServer()) {
0906: return sipRequest.getFromHeader().getAddress();
0907: } else {
0908: return sipRequest.getTo().getAddress();
0909: }
0910: }
0911:
0912: /**
0913: * Returns the Address identifying the remote party.
0914: * This is the value of the ToHeader header of locally initiated
0915: * requests in this dialogue when acting as an User Agent Client.
0916: * <p>
0917: * This is the value of the FromHeader header of recieved responses in this
0918: * dialogue when acting as an User Agent Server.
0919: *
0920: * @return the address object of the remote party.
0921: */
0922: public Address getRemoteParty() {
0923: Request sipRequest = ((Transaction) this .getFirstTransaction())
0924: .getOriginalRequest();
0925: if (!isServer()) {
0926: return sipRequest.getTo().getAddress();
0927: } else {
0928: return sipRequest.getFromHeader().getAddress();
0929: }
0930: }
0931:
0932: /**
0933: * Returns the Address identifying the remote target.
0934: * This is the value of the Contact header of recieved Responses
0935: * for Requests or refresh Requests
0936: * in this dialogue when acting as an User Agent Client <p>
0937: * This is the value of the Contact header of received Requests
0938: * or refresh Requests in this dialogue when acting as an User
0939: *
0940: * @return the address object of the remote target.
0941: */
0942: public Address getRemoteTarget() {
0943: if (contactRoute == null) {
0944: return null;
0945: }
0946: return contactRoute.getAddress();
0947: }
0948:
0949: /**
0950: * Returns the current state of the dialogue. The states are as follows:
0951: * <ul>
0952: * <li> Early - A dialog is in the "early" state, which occurs when it is
0953: * created when a provisional response is recieved to the INVITE Request.
0954: * <li> Confirmed - A dialog transitions to the "confirmed" state when a 2xx
0955: * final response is received to the INVITE Request.
0956: * <li> Completed - A dialog transitions to the "completed" state when a BYE
0957: * request is sent or received by the User Agent Client.
0958: * <li> Terminated - A dialog transitions to the "terminated" state when it
0959: * can be garbage collection.
0960: * </ul>
0961: * Independent of the method, if a request outside of a dialog generates a
0962: * non-2xx final response, any early dialogs created through provisional
0963: * responses to that request are terminated. If no response arrives at all
0964: * on the early dialog, it also terminates.
0965: *
0966: * @return a DialogState determining the current state of the dialog.
0967: */
0968: public int getState() {
0969: return this .dialogState;
0970: }
0971:
0972: /**
0973: * Returns true if this Dialog is secure i.e. if the request arrived over
0974: * TLS, and the Request-URI contained a SIPS URI, the "secure" flag is set
0975: * to TRUE.
0976: *
0977: * @return <code>true</code> if this dialogue was established using a sips
0978: * URI over TLS, and <code>false</code> otherwise.
0979: */
0980: public boolean isSecure() {
0981: return Utils.equalsIgnoreCase(getFirstTransaction()
0982: .getRequest().getRequestURI().getScheme(),
0983: SIPConstants.SCHEME_SIPS);
0984: }
0985:
0986: /**
0987: * Sends ACK Request to the remote party of this Dialogue.
0988: *
0989: * @param request the new ACK Request message to send.
0990: * @throws SipException if implementation cannot send the ACK Request for
0991: * any other reason
0992: */
0993: public void sendAck(Request request) throws SipException {
0994: Request ackRequest = (Request) request;
0995:
0996: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0997: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0998: "sendAck" + this );
0999: }
1000:
1001: if (isServer()) {
1002: throw new SipException("Cannot sendAck from "
1003: + "Server side of Dialog",
1004: SipException.DIALOG_UNAVAILABLE);
1005: }
1006:
1007: if (!ackRequest.getMethod().equals(Request.ACK)) {
1008: throw new SipException(
1009: "Bad request method -- should be ACK",
1010: SipException.INVALID_MESSAGE);
1011: }
1012:
1013: if (getState() == -1 || getState() == EARLY_STATE) {
1014: throw new SipException("Bad dialog state " + getState(),
1015: SipException.INVALID_STATE);
1016: }
1017:
1018: if (!((Transaction) getFirstTransaction()).getOriginalRequest()
1019: .getCallId().getCallId().equals(
1020: ((Request) request).getCallId().getCallId())) {
1021: throw new SipException("Bad call ID in request",
1022: SipException.INVALID_MESSAGE);
1023: }
1024:
1025: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
1026: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
1027: "setting from tag For outgoing ACK = "
1028: + this .getLocalTag());
1029: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
1030: "setting ToHeader tag for outgoing ACK = "
1031: + this .getRemoteTag());
1032: }
1033:
1034: if (this .getLocalTag() != null)
1035: ackRequest.getFromHeader().setTag(this .getLocalTag());
1036: if (this .getRemoteTag() != null)
1037: ackRequest.getTo().setTag(this .getRemoteTag());
1038:
1039: // Create the route request and set it appropriately.
1040: // Note that we only need to worry about being on the client
1041: // side of the request.
1042: buildRouteSet(ackRequest);
1043:
1044: Hop hop = getNextHop();
1045:
1046: try {
1047: MessageChannel messageChannel = sipStack
1048: .createRawMessageChannel(hop);
1049: if (messageChannel == null) {
1050: // procedures of 8.1.2 and 12.2.1.1 of RFC3261 have
1051: // been tried but the resulting next hop cannot be
1052: // resolved (recall that the exception thrown is
1053: // caught and ignored in
1054: // SIPMessageStack.createMessageChannel() so we end
1055: // up here with a null messageChannel instead of the
1056: // exception handler below). All else failing, try
1057: // the outbound proxy in accordance with 8.1.2, in
1058: // particular: This ensures that outbound proxies
1059: // that do not add Record-Route header field values
1060: // will drop out of the path of subsequent requests.
1061: // It allows endpoints that cannot resolve the first
1062: // Route URI to delegate that task to an outbound
1063: // proxy.
1064: //
1065: // if one considers the 'first Route URI' of a
1066: // request constructed according to 12.2.1.1
1067: // to be the request URI when the route set is empty.
1068: Hop outboundProxy = sipStack.getRouter()
1069: .getOutboundProxy();
1070: if (outboundProxy == null) {
1071: throw new SipException("No route found!",
1072: SipException.GENERAL_ERROR);
1073: }
1074:
1075: messageChannel = sipStack
1076: .createRawMessageChannel(outboundProxy);
1077: }
1078: // Wrap a client transaction around the raw message channel.
1079: ClientTransaction clientTransaction = (ClientTransaction) sipStack
1080: .createMessageChannel(messageChannel);
1081: clientTransaction.setOriginalRequest(ackRequest);
1082: clientTransaction.sendMessage((Message) ackRequest);
1083: // Do not retransmit the ACK so terminate the transaction
1084: // immediately.
1085: this .lastAck = ackRequest;
1086: clientTransaction.setState(Transaction.TERMINATED_STATE);
1087: } catch (IOException ex) {
1088: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
1089: Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
1090: "Exception occured: " + ex);
1091: // ex.printStackTrace();
1092: }
1093:
1094: throw new SipException("Cold not create message channel",
1095: SipException.GENERAL_ERROR);
1096: }
1097: }
1098:
1099: /**
1100: * Creates a new Request message based on the dialog creating request.
1101: * This method should be used for but not limited to creating Bye's,
1102: * Refer's and re-Invite's on the Dialog. The returned Request will be
1103: * correctly formatted that is it will contain the correct
1104: * CSeqHeader header,
1105: * Route headers and requestURI (derived from the remote target). This
1106: * method should not be used for Ack, that is the application should
1107: * create the Ack from the MessageFactory.
1108: *
1109: * If the route set is not empty, and the first URI in the route set
1110: * contains the lr parameter (see Section 19.1.1), the UAC MUST place
1111: * the remote target URI into the Request-URI and MUST include a Route
1112: * header field containing the route set values in order, including all
1113: * parameters.
1114: * If the route set is not empty, and its first URI does not contain the
1115: * lr parameter, the UAC MUST place the first URI from the route set
1116: * into the Request-URI, stripping any parameters that are not allowed
1117: * in a Request-URI. The UAC MUST add a Route header field containing
1118: * the remainder of the route set values in order, including all
1119: * parameters. The UAC MUST then place the remote target URI into the
1120: * Route header field as the last value.
1121: *
1122: * @param method the string value that determines if the request to be
1123: * created.
1124: * @return the newly created Request message on this Dialog.
1125: * @throws SipException if the Dialog is not yet established.
1126: */
1127: public Request createRequest(String method) throws SipException {
1128: // Set the dialog back pointer.
1129: if (method == null)
1130: throw new NullPointerException("null method");
1131: else if (this .getState() == -1
1132: || ((!method.equals(Request.BYE)) && this .getState() == TERMINATED_STATE)
1133: || (method.equals(Request.BYE) && this .getState() == EARLY_STATE)) {
1134: throw new SipException(
1135: "Dialog not yet established or terminated",
1136: SipException.DIALOG_UNAVAILABLE);
1137: }
1138:
1139: Request originalRequest = (Request) getFirstTransaction()
1140: .getRequest();
1141:
1142: RequestLine requestLine = new RequestLine();
1143: requestLine.setUri((URI) getRemoteParty().getURI());
1144: requestLine.setMethod(method);
1145:
1146: Request sipRequest = originalRequest.createRequest(requestLine,
1147: isServer());
1148:
1149: // Guess of local sequence number - this is being re-set when
1150: // the request is actually dispatched (reported by Brad Templeton
1151: // and Antonis Karydas).
1152: if (!method.equals(Request.ACK)) {
1153: CSeqHeader cseq = (CSeqHeader) sipRequest.getCSeqHeader();
1154: cseq.setSequenceNumber(this .localSequenceNumber + 1);
1155: }
1156:
1157: if (isServer()) {
1158: // Remove the old via headers.
1159: sipRequest.removeHeader(Header.VIA);
1160: // Add a via header for the outbound request based on the
1161: // transport of the message processor.
1162:
1163: // MessageProcessor messageProcessor = sipStack.getMessageProcessor(
1164: // firstTransaction.encapsulatedChannel.getTransport());
1165: MessageProcessor messageProcessor = firstTransaction.encapsulatedChannel
1166: .getMessageProcessor();
1167:
1168: ViaHeader via = messageProcessor.getViaHeader();
1169: sipRequest.addHeader(via);
1170: }
1171:
1172: FromHeader from = (FromHeader) sipRequest.getFromHeader();
1173: ToHeader to = (ToHeader) sipRequest.getTo();
1174:
1175: from.setTag(getLocalTag());
1176: to.setTag(getRemoteTag());
1177:
1178: // RFC 3261, p. 75:
1179: // A UAC SHOULD include a Contact header field in any target refresh
1180: // requests within a dialog, and unless there is a need to change it,
1181: // the URI SHOULD be the same as used in previous requests within the
1182: // dialog.
1183: ContactList cl = originalRequest.getContactHeaders();
1184: if (cl != null) {
1185: sipRequest.addHeader((ContactList) (cl.clone()));
1186: }
1187:
1188: // get the route list from the dialog.
1189: buildRouteSet(sipRequest);
1190:
1191: return sipRequest;
1192: }
1193:
1194: /**
1195: * Sends a Request to the remote party of this dialog. This method
1196: * implies that the application is functioning as UAC hence the
1197: * underlying SipProvider acts statefully. This method is useful for
1198: * sending Bye's for terminating a dialog or Re-Invites on the Dialog
1199: * for third party call control.
1200: * <p>
1201: * This methods will set the FromHeader and the ToHeader tags for
1202: * the outgoing
1203: * request and also set the correct sequence number to the outgoing
1204: * Request and associate the client transaction with this dialog.
1205: * Note that any tags assigned by the user will be over-written by this
1206: * method.
1207: * <p>
1208: * The User Agent must not send a BYE on a confirmed INVITE until it has
1209: * received an ACK for its 2xx response or until the server transaction
1210: * timeout is received.
1211: * <p>
1212: * When the retransmissionFilter is <code>true</code>,
1213: * that is the SipProvider takes care of all retransmissions for the
1214: * application, and the SipProvider can not deliver the Request after
1215: * multiple retransmits the SipListener will be notified with a
1216: * {@link TimeoutEvent} when the transaction expires.
1217: * @param clientTransactionId the new ClientTransaction object identifying
1218: * this transaction, this clientTransaction should be requested from
1219: * SipProvider.getNewClientTransaction
1220: * @throws TransactionDoesNotExistException if the serverTransaction does
1221: * not correspond to any existing server transaction.
1222: * @throws SipException if implementation cannot send the Request for
1223: * any reason.
1224: */
1225: public void sendRequest(ClientTransaction clientTransactionId)
1226: throws SipException {
1227: if (clientTransactionId == null) {
1228: throw new NullPointerException("null parameter");
1229: }
1230:
1231: Request dialogRequest = clientTransactionId
1232: .getOriginalRequest();
1233: String method = dialogRequest.getMethod();
1234:
1235: if (method.equals(Request.ACK) || method.equals(Request.CANCEL)) {
1236: throw new SipException("Bad Request Method: "
1237: + dialogRequest.getMethod(),
1238: SipException.INVALID_MESSAGE);
1239: }
1240:
1241: // Cannot send bye until the dialog has been established.
1242: if (getState() == INITIAL_STATE) {
1243: throw new SipException("Bad dialog state (-1).",
1244: SipException.DIALOG_UNAVAILABLE);
1245: }
1246:
1247: if (Utils.equalsIgnoreCase(dialogRequest.getMethod(),
1248: Request.BYE)
1249: && getState() == EARLY_STATE) {
1250: throw new SipException("Bad dialog state ",
1251: SipException.DIALOG_UNAVAILABLE);
1252: }
1253:
1254: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
1255: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
1256: "dialog.sendRequest " + " dialog = " + this
1257: + "\ndialogRequest = \n" + dialogRequest);
1258: }
1259:
1260: if (dialogRequest.getTopmostVia() == null) {
1261: ViaHeader via = ((ClientTransaction) clientTransactionId)
1262: .getOutgoingViaHeader();
1263: dialogRequest.addHeader(via);
1264: }
1265:
1266: if (!((Transaction) this .getFirstTransaction())
1267: .getOriginalRequest().getCallId().getCallId().equals(
1268: dialogRequest.getCallId().getCallId())) {
1269: throw new SipException("Bad call ID in request",
1270: SipException.INVALID_MESSAGE);
1271: }
1272:
1273: // Set the dialog back pointer.
1274: ((ClientTransaction) clientTransactionId).setDialog(this );
1275:
1276: FromHeader from = (FromHeader) dialogRequest.getFromHeader();
1277: ToHeader to = (ToHeader) dialogRequest.getTo();
1278:
1279: from.setTag(this .getLocalTag());
1280: to.setTag(this .getRemoteTag());
1281:
1282: // Caller has not assigned the route header - set the route header
1283: // and the request URI for the outgoing request.
1284: // Bugs reported by Brad Templeton.
1285:
1286: buildRouteSet(dialogRequest);
1287:
1288: Hop hop = this .getNextHop();
1289:
1290: try {
1291: MessageChannel messageChannel = sipStack
1292: .createRawMessageChannel(hop);
1293: ((ClientTransaction) clientTransactionId).encapsulatedChannel = messageChannel;
1294:
1295: if (messageChannel == null) {
1296: // procedures of 8.1.2 and 12.2.1.1 of RFC3261 have
1297: // been tried but the resulting next hop cannot be
1298: // resolved (recall that the exception thrown is
1299: // caught and ignored in
1300: // SIPMessageStack.createMessageChannel() so we end
1301: // up here with a null messageChannel instead of the
1302: // exception handler below). All else failing, try
1303: // the outbound proxy in accordance with 8.1.2, in
1304: // particular: This ensures that outbound proxies
1305: // that do not add Record-Route header field values
1306: // will drop out of the path of subsequent requests.
1307: // It allows endpoints that cannot resolve the first
1308: // Route URI to delegate that task to an outbound
1309: // proxy.
1310: //
1311: // if one considers the 'first Route URI' of a
1312: // request constructed according to 12.2.1.1
1313: // to be the request URI when the route set is empty.
1314: Hop outboundProxy = sipStack.getRouter()
1315: .getOutboundProxy();
1316: if (outboundProxy == null) {
1317: throw new SipException("No route found!",
1318: SipException.GENERAL_ERROR);
1319: }
1320: messageChannel = sipStack
1321: .createRawMessageChannel(outboundProxy);
1322: }
1323:
1324: ((ClientTransaction) clientTransactionId).encapsulatedChannel = messageChannel;
1325: } catch (IOException ex) {
1326: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
1327: Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
1328: "Exception occured: " + ex);
1329: }
1330:
1331: throw new SipException("Could not create message channel.",
1332: SipException.GENERAL_ERROR);
1333: }
1334:
1335: // Increment before setting!!
1336: localSequenceNumber++;
1337: dialogRequest.getCSeqHeader().setSequenceNumber(
1338: getLocalSequenceNumber());
1339:
1340: if (this .isServer()) {
1341: // ServerTransaction serverTransaction = (ServerTransaction)
1342: // getFirstTransaction();
1343:
1344: from.setTag(this .myTag);
1345: to.setTag(this .hisTag);
1346:
1347: try {
1348: ((ClientTransaction) clientTransactionId)
1349: .sendMessage(dialogRequest);
1350: // If the method is BYE then mark the dialog completed.
1351: if (dialogRequest.getMethod().equals(Request.BYE)) {
1352: setState(COMPLETED_STATE);
1353: }
1354: } catch (IOException ex) {
1355: throw new SipException("Error sending message.",
1356: SipException.GENERAL_ERROR);
1357: }
1358: } else {
1359: // I am the client so I do not swap headers.
1360: // ClientTransaction clientTransaction = (ClientTransaction)
1361: // getFirstTransaction();
1362:
1363: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
1364: Logging.report(Logging.INFORMATION,
1365: LogChannels.LC_JSR180, "setting tags from "
1366: + this .getDialogId());
1367: Logging.report(Logging.INFORMATION,
1368: LogChannels.LC_JSR180, "fromTag " + this .myTag);
1369: Logging.report(Logging.INFORMATION,
1370: LogChannels.LC_JSR180, "toTag " + this .hisTag);
1371: }
1372:
1373: from.setTag(this .myTag);
1374: to.setTag(this .hisTag);
1375:
1376: try {
1377: ((ClientTransaction) clientTransactionId)
1378: .sendMessage(dialogRequest);
1379:
1380: // If the method is BYE then mark the dialog completed.
1381: if (dialogRequest.getMethod().equals(Request.BYE)) {
1382: this .setState(COMPLETED_STATE);
1383: }
1384: } catch (IOException ex) {
1385: throw new SipException("Error sending message.",
1386: SipException.GENERAL_ERROR);
1387: }
1388: }
1389: }
1390:
1391: /**
1392: * Returns yes if the last response is to be retransmitted.
1393: * @return true if final response retransmitted
1394: */
1395: protected boolean toRetransmitFinalResponse() {
1396: if (--retransmissionTicksLeft == 0) {
1397: retransmissionTicksLeft = 2 * prevRetransmissionTicks;
1398: prevRetransmissionTicks = retransmissionTicksLeft;
1399: return true;
1400: } else {
1401: return false;
1402: }
1403: }
1404:
1405: /**
1406: * Sets retransmission ticks.
1407: */
1408: protected void setRetransmissionTicks() {
1409: retransmissionTicksLeft = 1;
1410: prevRetransmissionTicks = 1;
1411: }
1412:
1413: /**
1414: * Resends the last ack.
1415: */
1416: public void resendAck() {
1417: // Check for null.
1418: try {
1419: if (lastAck != null)
1420: sendAck(lastAck);
1421: } catch (SipException ex) {
1422: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
1423: Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
1424: "Dialog.resendAck(): SipException occured:"
1425: + ex);
1426: ex.printStackTrace();
1427: }
1428: }
1429: }
1430:
1431: /**
1432: * Checks if this is an invitation dialog.
1433: * @return true if this is an invitation dialog
1434: */
1435: public boolean isInviteDialog() {
1436: return getFirstTransaction().getRequest().getMethod().equals(
1437: Request.INVITE);
1438: }
1439:
1440: /**
1441: * Checks if this is a subscription dialog.
1442: * @return true if this is a subscription dialog
1443: */
1444: public boolean isSubscribeDialog() {
1445: String method = getFirstTransaction().getRequest().getMethod();
1446: // REFER creates an implicit subscription
1447: return (method.equals(Request.SUBSCRIBE) || method
1448: .equals(Request.REFER));
1449: }
1450: }
|