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: package gov.nist.siplite.stack;
0026:
0027: import gov.nist.microedition.sip.SipClientConnectionImpl;
0028: import gov.nist.siplite.message.*;
0029: import gov.nist.siplite.header.*;
0030: import gov.nist.siplite.address.*;
0031: import gov.nist.core.*;
0032: import java.util.*;
0033: import java.io.IOException;
0034:
0035: import com.sun.midp.log.Logging;
0036: import com.sun.midp.log.LogChannels;
0037:
0038: /**
0039: * Adds a transaction layer to the {@link SIPMessageStack} class. This
0040: * is done by
0041: * replacing the normal MessageChannels returned by the base class with
0042: * transaction-aware MessageChannels that encapsulate the original channels
0043: * and handle the transaction state machine, retransmissions, etc.
0044: *
0045: * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
0046: *
0047: * @version JAIN-SIP-1.1
0048: *
0049: */
0050: public abstract class SIPTransactionStack extends SIPMessageStack
0051: implements SIPTransactionEventListener {
0052:
0053: /**
0054: * Number of milliseconds between timer ticks (500).
0055: */
0056: public static final int BASE_TIMER_INTERVAL = 500;
0057:
0058: /** Collection of current client transactions. */
0059: private Vector clientTransactions;
0060: /** Collection or current server transactions. */
0061: private Vector serverTransactions;
0062: /** Table of dialogs. */
0063: private Hashtable dialogTable;
0064:
0065: /** Max number of server transactions concurrent. */
0066: protected int transactionTableSize;
0067:
0068: /**
0069: * Retransmission filter - indicates the stack will retransmit
0070: * 200 OK for invite transactions.
0071: */
0072: protected boolean retransmissionFilter;
0073:
0074: /** A set of methods that result in dialog creations. */
0075: protected Hashtable dialogCreatingMethods;
0076:
0077: /** Default constructor. */
0078: protected SIPTransactionStack() {
0079: super ();
0080: this .transactionTableSize = -1;
0081: // a set of methods that result in dialog creation.
0082: this .dialogCreatingMethods = new Hashtable();
0083: // Standard set of methods that create dialogs.
0084: this .dialogCreatingMethods.put(Request.REFER, "");
0085: this .dialogCreatingMethods.put(Request.INVITE, "");
0086: this .dialogCreatingMethods.put(Request.SUBSCRIBE, "");
0087: // Notify may or may not create a dialog. This is handled in
0088: // the code.
0089: // this.dialogCreatingMethods.add(Request.NOTIFY);
0090: // this.dialogCreatingMethods.put(Request.MESSAGE, "");
0091: // Create the transaction collections
0092: clientTransactions = new Vector();
0093: serverTransactions = new Vector();
0094: // Dialog dable.
0095: this .dialogTable = new Hashtable();
0096:
0097: // Start the timer event thread.
0098: // System.out.println("Starting timeout");
0099: new Thread(new TransactionScanner()).start();
0100:
0101: }
0102:
0103: /**
0104: * Prints the Dialog creating methods.
0105: */
0106: private void printDialogCreatingMethods() {
0107: System.out.println("PRINTING DIALOGCREATINGMETHODS HASHTABLE");
0108: Enumeration e = dialogCreatingMethods.keys();
0109: while (e.hasMoreElements()) {
0110: System.out.println(e.nextElement());
0111: }
0112: System.out.println("DIALOGCREATINGMETHODS HASHTABLE PRINTED");
0113: }
0114:
0115: /**
0116: * Returns true if extension is supported.
0117: * @param method the name of the method used for create
0118: * @return true if extension is supported and false otherwise.
0119: */
0120: public boolean isDialogCreated(String method) {
0121: // printDialogCreatingMethods();
0122: // System.out.println("CHECKING IF DIALOG HAS BEEN CREATED"
0123: // + " FOR THE FOLLOWING METHOD"
0124: // + method.toUpperCase());
0125: return dialogCreatingMethods.containsKey(method.toUpperCase());
0126: }
0127:
0128: /**
0129: * Returns true if method can change dialog state.
0130: * @param method the name of the method used for create
0131: * @return true if extension is supported and false otherwise.
0132: */
0133: public boolean allowDialogStateChange(String method) {
0134: return dialogCreatingMethods.containsKey(method.toUpperCase())
0135: || method.equalsIgnoreCase(Request.BYE)
0136: || method.equalsIgnoreCase(Request.NOTIFY);
0137: }
0138:
0139: /**
0140: * Adds an extension method.
0141: * @param extensionMethod -- extension method to support for dialog
0142: * creation
0143: */
0144: public void addExtensionMethod(String extensionMethod) {
0145: if (!extensionMethod.equals(Request.NOTIFY)) {
0146: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0147: Logging.report(Logging.INFORMATION,
0148: LogChannels.LC_JSR180,
0149: "NOTIFY Supported Natively");
0150: }
0151: } else {
0152: this .dialogCreatingMethods.put(extensionMethod, "");
0153: }
0154: }
0155:
0156: /**
0157: * Puts a dialog into the dialog table.
0158: * @param dialog -- dialog to put into the dialog table.
0159: */
0160: public void putDialog(Dialog dialog) {
0161: String dialogId = dialog.getDialogId();
0162:
0163: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0164: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0165: "putDialog dialogId=" + dialogId);
0166: }
0167:
0168: // if (this.getDefaultRouteHeader() != null)
0169: // dialog.addRoute(this.getDefaultRouteHeader(), false);
0170: dialog.setStack(this );
0171:
0172: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0173: // new Exception().printStackTrace();
0174: }
0175:
0176: synchronized (dialogTable) {
0177: dialogTable.put(dialogId, dialog);
0178: }
0179:
0180: }
0181:
0182: /**
0183: * Creates a new dialog for requested transaction.
0184: * @param transaction the requested transaction
0185: * @return the new Dialog object
0186: */
0187: public synchronized Dialog createDialog(Transaction transaction) {
0188: Request sipRequest = transaction.getOriginalRequest();
0189: Dialog retval = new Dialog(transaction);
0190:
0191: return retval;
0192: }
0193:
0194: /**
0195: * Returns the dialog for a given dialog ID. If compatibility is
0196: * enabled then we do not assume the presence of tags and hence
0197: * need to add a flag to indicate whether this is a server or
0198: * client transaction.
0199: * @param dialogId is the dialog id to check.
0200: * @return the Dialog object for the requested id
0201: */
0202: public Dialog getDialog(String dialogId) {
0203: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0204: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0205: "Getting dialog for " + dialogId);
0206: }
0207:
0208: synchronized (dialogTable) {
0209: return (Dialog) dialogTable.get(dialogId);
0210: }
0211: }
0212:
0213: /**
0214: * Finds a matching client SUBSCRIBE to the incoming notify.
0215: * NOTIFY requests are matched to such SUBSCRIBE requests if they
0216: * contain the same "Call-ID", a "ToHeader" header "tag" parameter which
0217: * matches the "FromHeader" header "tag" parameter of the SUBSCRIBE, and the
0218: * same "Event" header field. Rules for comparisons of the "Event"
0219: * headers are described in section 7.2.1. If a matching NOTIFY request
0220: * contains a "Subscription-State" of "active" or "pending", it creates
0221: * a new subscription and a new dialog (unless they have already been
0222: * created by a matching response, as described above).
0223: *
0224: * @param notifyMessage the request to be matched
0225: * @return the new client transaction object
0226: */
0227: public ClientTransaction findSubscribeTransaction(
0228: Request notifyMessage) {
0229: synchronized (clientTransactions) {
0230: Enumeration it = clientTransactions.elements();
0231: String this ToHeaderTag = notifyMessage.getTo().getTag();
0232: if (this ToHeaderTag == null)
0233: return null;
0234: EventHeader eventHdr = (EventHeader) notifyMessage
0235: .getHeader(Header.EVENT);
0236: if (eventHdr == null)
0237: return null;
0238: while (it.hasMoreElements()) {
0239: ClientTransaction ct = (ClientTransaction) it
0240: .nextElement();
0241: Request sipRequest = ct.getOriginalRequest();
0242: String fromTag = sipRequest.getFromHeader().getTag();
0243: EventHeader hisEvent = (EventHeader) sipRequest
0244: .getHeader(Header.EVENT);
0245: // Event header is mandatory but some slopply clients
0246: // dont include it.
0247: if (hisEvent == null)
0248: continue;
0249: if (sipRequest.getMethod().equals(Request.SUBSCRIBE)
0250: && Utils.equalsIgnoreCase(fromTag,
0251: this ToHeaderTag)
0252: && hisEvent != null
0253: && eventHdr.match(hisEvent)
0254: && Utils.equalsIgnoreCase(notifyMessage
0255: .getCallId().getCallId(), sipRequest
0256: .getCallId().getCallId()))
0257: return ct;
0258: }
0259:
0260: }
0261: return null;
0262: }
0263:
0264: /**
0265: * Finds the transaction corresponding to a given request.
0266: * @param sipMessage request for which to retrieve the transaction.
0267: * @param isServer search the server transaction table if true.
0268: * @return the transaction object corresponding to the request or null
0269: * if no such mapping exists.
0270: */
0271: public Transaction findTransaction(Message sipMessage,
0272: boolean isServer) {
0273:
0274: if (isServer) {
0275: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0276: Logging.report(Logging.INFORMATION,
0277: LogChannels.LC_JSR180,
0278: "searching server transaction for "
0279: + sipMessage + " size = "
0280: + this .serverTransactions.size());
0281: }
0282:
0283: synchronized (this .serverTransactions) {
0284: Enumeration it = serverTransactions.elements();
0285: while (it.hasMoreElements()) {
0286: ServerTransaction sipServerTransaction = (ServerTransaction) it
0287: .nextElement();
0288: if (sipServerTransaction
0289: .isMessagePartOfTransaction(sipMessage))
0290: return sipServerTransaction;
0291: }
0292: }
0293: } else {
0294: synchronized (this .clientTransactions) {
0295: Enumeration it = clientTransactions.elements();
0296: while (it.hasMoreElements()) {
0297: ClientTransaction clientTransaction = (ClientTransaction) it
0298: .nextElement();
0299: if (clientTransaction
0300: .isMessagePartOfTransaction(sipMessage))
0301: return clientTransaction;
0302: }
0303: }
0304:
0305: }
0306: return null;
0307:
0308: }
0309:
0310: /**
0311: * Gets the transaction to cancel. Search the server transaction
0312: * table for a transaction that matches the given transaction.
0313: * @param cancelRequest the request to be found
0314: * @param isServer true if this is a server request
0315: * @return the transaction object requested
0316: */
0317: public Transaction findCancelTransaction(Request cancelRequest,
0318: boolean isServer) {
0319:
0320: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0321: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
0322: "findCancelTransaction request = \n"
0323: + cancelRequest
0324: + "\nfindCancelRequest isServer = "
0325: + isServer);
0326: }
0327:
0328: if (isServer) {
0329: synchronized (this .serverTransactions) {
0330: Enumeration li = this .serverTransactions.elements();
0331: while (li.hasMoreElements()) {
0332: Transaction transaction = (Transaction) li
0333: .nextElement();
0334: Request sipRequest = (Request) (transaction
0335: .getRequest());
0336: ServerTransaction sipServerTransaction = (ServerTransaction) transaction;
0337: if (sipServerTransaction
0338: .doesCancelMatchTransaction(cancelRequest))
0339: return sipServerTransaction;
0340: }
0341: }
0342: } else {
0343: synchronized (this .clientTransactions) {
0344: Enumeration li = this .clientTransactions.elements();
0345: while (li.hasMoreElements()) {
0346: Transaction transaction = (Transaction) li
0347: .nextElement();
0348: Request sipRequest = (Request) (transaction
0349: .getRequest());
0350:
0351: ClientTransaction sipClientTransaction = (ClientTransaction) transaction;
0352: if (sipClientTransaction
0353: .doesCancelMatchTransaction(cancelRequest))
0354: return sipClientTransaction;
0355:
0356: }
0357: }
0358: }
0359: return null;
0360: }
0361:
0362: /**
0363: * Construcor for the stack. Registers the request and response
0364: * factories for the stack.
0365: * @param messageFactory User-implemented factory for processing
0366: * messages.
0367: */
0368: protected SIPTransactionStack(SIPStackMessageFactory messageFactory) {
0369: this ();
0370: super .sipMessageFactory = messageFactory;
0371: }
0372:
0373: /**
0374: * Thread used to throw timer events for all transactions.
0375: */
0376: class TransactionScanner implements Runnable {
0377: /**
0378: * Main transaction scanner processing loop.
0379: */
0380: public void run() {
0381:
0382: // Iterator through all transactions
0383: Enumeration transactionIterator;
0384: // One transaction in the set
0385: Transaction nextTransaction;
0386:
0387: // Loop while this stack is running
0388: while (isAlive()) {
0389: try {
0390: // Sleep for one timer "tick"
0391: Thread.sleep(BASE_TIMER_INTERVAL);
0392:
0393: // System.out.println("clientTransactionTable size " +
0394: // clientTransactions.size());
0395: // System.out.println("serverTransactionTable size " +
0396: // serverTransactions.size());
0397: // Check all client transactions
0398:
0399: Vector fireList = new Vector();
0400: Vector removeList = new Vector();
0401:
0402: // Check all server transactions
0403: synchronized (serverTransactions) {
0404: transactionIterator = serverTransactions
0405: .elements();
0406: while (transactionIterator.hasMoreElements()) {
0407:
0408: nextTransaction = (Transaction) transactionIterator
0409: .nextElement();
0410:
0411: // If the transaction has terminated,
0412: if (nextTransaction.isTerminated()) {
0413: // Keep the transaction hanging around
0414: // to catch the incoming ACK.
0415: if (((ServerTransaction) nextTransaction).collectionTime == 0) {
0416: // Remove it from the set
0417: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0418: Logging
0419: .report(
0420: Logging.INFORMATION,
0421: LogChannels.LC_JSR180,
0422: "removing"
0423: + nextTransaction);
0424: }
0425: removeList
0426: .addElement(nextTransaction);
0427: } else {
0428: ((ServerTransaction) nextTransaction).collectionTime--;
0429: }
0430: // If this transaction has not
0431: // terminated,
0432: } else {
0433: // Add to the fire list -- needs to be moved
0434: // outside the synchronized block to prevent
0435: // deadlock.
0436: fireList.addElement(nextTransaction);
0437:
0438: }
0439:
0440: }
0441: for (int j = 0; j < removeList.size(); j++) {
0442: serverTransactions.removeElement(removeList
0443: .elementAt(j));
0444: }
0445: }
0446:
0447: removeList = new Vector();
0448:
0449: synchronized (clientTransactions) {
0450: transactionIterator = clientTransactions
0451: .elements();
0452: while (transactionIterator.hasMoreElements()) {
0453:
0454: nextTransaction = (Transaction) transactionIterator
0455: .nextElement();
0456:
0457: // If the transaction has terminated,
0458: // and SipClientConnection instance,
0459: // associated with it has TERMINATED state
0460: boolean removeTransaction = false;
0461: if (nextTransaction.isTerminated()) {
0462: SipClientConnectionImpl conn = (SipClientConnectionImpl) nextTransaction
0463: .getApplicationData();
0464: if (conn == null) { // no connection
0465: removeTransaction = true;
0466: } else if (conn.getState() == SipClientConnectionImpl.TERMINATED) {
0467: removeTransaction = true;
0468: }
0469: }
0470: if (removeTransaction) {
0471: // Remove it from the set
0472: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0473: Logging.report(Logging.INFORMATION,
0474: LogChannels.LC_JSR180,
0475: "Removing clientTransaction "
0476: + nextTransaction);
0477: }
0478:
0479: removeList.addElement(nextTransaction);
0480:
0481: // If this transaction has not
0482: // terminated,
0483: } else {
0484: // Add to the fire list -- needs to be moved
0485: // outside the synchronized block to prevent
0486: // deadlock.
0487: fireList.addElement(nextTransaction);
0488:
0489: }
0490: }
0491: for (int j = 0; j < removeList.size(); j++) {
0492: clientTransactions.removeElement(removeList
0493: .elementAt(j));
0494: }
0495: }
0496: removeList = new Vector();
0497:
0498: synchronized (dialogTable) {
0499: Enumeration values = dialogTable.elements();
0500: while (values.hasMoreElements()) {
0501: Dialog d = (Dialog) values.nextElement();
0502: // System.out.println("dialogState = " +
0503: // d.getState() +
0504: // " isServer = " + d.isServer());
0505: if (d.getState() == Dialog.TERMINATED_STATE) {
0506: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0507: String dialogId = d.getDialogId();
0508: Logging.report(Logging.INFORMATION,
0509: LogChannels.LC_JSR180,
0510: "Removing Dialog "
0511: + dialogId);
0512: }
0513:
0514: removeList.addElement(d);
0515: }
0516:
0517: if (d.isServer() && (!d.ackSeen)
0518: && d.isInviteDialog()) {
0519: Transaction transaction = d
0520: .getLastTransaction();
0521: // If stack is managing the transaction
0522: // then retransmit the last response.
0523: if (transaction.getState() == Transaction.TERMINATED_STATE
0524: && transaction instanceof ServerTransaction
0525: && ((ServerTransaction) transaction).isMapped) {
0526: Response response = transaction
0527: .getLastResponse();
0528: // Retransmit to 200 until ack received.
0529: if (response.getStatusCode() == 200) {
0530: try {
0531: if (d
0532: .toRetransmitFinalResponse())
0533: transaction
0534: .sendMessage(response);
0535: } catch (IOException ex) {
0536: /* Will eventully time out */
0537: d
0538: .setState(Dialog.TERMINATED_STATE);
0539: } finally {
0540: // Need to fire the timer so
0541: // transaction will eventually
0542: // time out whether or not
0543: // the IOException occurs
0544: fireList
0545: .addElement(transaction);
0546: }
0547: }
0548: }
0549: }
0550:
0551: }
0552: for (int j = 0; j < removeList.size(); j++) {
0553: Dialog d = (Dialog) removeList.elementAt(j);
0554: dialogTable.remove(d.getDialogId());
0555: }
0556: }
0557:
0558: for (int i = 0; i < fireList.size(); i++) {
0559: nextTransaction = (Transaction) fireList
0560: .elementAt(i);
0561: nextTransaction.fireTimer();
0562: }
0563:
0564: } catch (Exception e) {
0565: e.printStackTrace();
0566: // Ignore
0567: }
0568:
0569: }
0570:
0571: }
0572: }
0573:
0574: /**
0575: * Creates or terminates a dialog if a subscription matching the given
0576: * NOTIFY request exists. To create a dialog a list of transactions
0577: * is searched for the matching transaction and then the corresponding
0578: * SipClientConnection is notified. To terminate a dialog a list of
0579: * dialogs is searched for the existing subscription that matches
0580: * the given NOTIFY request.
0581: * @param requestReceived NOTIFY request that may create or terminate
0582: * a dialog.
0583: */
0584: private void createOrTerminateDialog(Request requestReceived) {
0585: // System.out.println(">>> NOTIFY: Scanning dialogs...");
0586:
0587: synchronized (dialogTable) {
0588: Enumeration e = dialogTable.elements();
0589:
0590: while (e.hasMoreElements()) {
0591: Dialog nextDialog = (Dialog) e.nextElement();
0592: Subscription s = nextDialog.subscriptionList
0593: .getMatchingSubscription(requestReceived);
0594:
0595: if (s != null) {
0596: SubscriptionStateHeader ssh = (SubscriptionStateHeader) requestReceived
0597: .getHeader(Header.SUBSCRIPTION_STATE);
0598:
0599: if (ssh != null && ssh.isTerminated()) {
0600: nextDialog.subscriptionList
0601: .removeSubscription(s);
0602: } else {
0603: nextDialog.setState(Dialog.CONFIRMED_STATE);
0604: }
0605:
0606: return;
0607: }
0608: }
0609: }
0610:
0611: // System.out.println(">>> NOTIFY: Scanning transactions...");
0612:
0613: // Iterator through all client transactions
0614: Enumeration transactionIterator;
0615: ClientTransaction currClientTransaction = null;
0616:
0617: // Loop through all client transactions
0618: synchronized (clientTransactions) {
0619: transactionIterator = clientTransactions.elements();
0620: currClientTransaction = null;
0621:
0622: String receivedToTag = requestReceived.getToTag();
0623: CallIdHeader receivedCid = requestReceived.getCallId();
0624: EventHeader receivedEvent = (EventHeader) requestReceived
0625: .getHeader(Header.EVENT);
0626:
0627: while (transactionIterator.hasMoreElements()) {
0628: currClientTransaction = (ClientTransaction) transactionIterator
0629: .nextElement();
0630: Request request = currClientTransaction.getRequest();
0631: String method = request.getMethod();
0632: String fromTag = request.getFromHeaderTag();
0633: CallIdHeader cid = request.getCallId();
0634: EventHeader hEvent = (EventHeader) request
0635: .getHeader(Header.EVENT);
0636: boolean isSameEvent = (hEvent != null)
0637: && hEvent.match(receivedEvent);
0638:
0639: if (((method.equals(Request.SUBSCRIBE) && isSameEvent) || method
0640: .equals(Request.REFER))
0641: && fromTag != null
0642: && fromTag.equals(receivedToTag)
0643: && cid != null
0644: && cid.equals(receivedCid)) {
0645:
0646: SipClientConnectionImpl sipClientConnection = (SipClientConnectionImpl) currClientTransaction
0647: .getApplicationData();
0648:
0649: if (sipClientConnection != null) {
0650: sipClientConnection
0651: .handleMatchingNotify(requestReceived);
0652: } else {
0653: if (Logging.REPORT_LEVEL <= Logging.WARNING) {
0654: Logging
0655: .report(
0656: Logging.WARNING,
0657: LogChannels.LC_JSR180,
0658: "SIPTransactionStack.createOrTerminateDialog(): "
0659: + "Cannot find SCC for the given NOTIFY.");
0660: }
0661: }
0662:
0663: break;
0664: }
0665: }
0666: }
0667: }
0668:
0669: /**
0670: * Handles a new SIP request.
0671: * It finds a server transaction to handle
0672: * this message. If none exists, it creates a new transaction.
0673: * @param requestReceived Request to handle.
0674: * @param requestMessageChannel Channel that received message.
0675: * @return A server transaction.
0676: */
0677: protected SIPServerRequestInterface newSIPServerRequest(
0678: Request requestReceived,
0679: MessageChannel requestMessageChannel) {
0680:
0681: try {
0682: // Iterator through all server transactions
0683: Enumeration transactionIterator;
0684: // Next transaction in the set
0685: ServerTransaction nextTransaction;
0686: // Transaction to handle this request
0687: ServerTransaction currentTransaction = null;
0688:
0689: if (requestReceived.getMethod().equals(Request.NOTIFY)) {
0690: createOrTerminateDialog(requestReceived);
0691: }
0692:
0693: // Loop through all server transactions
0694: synchronized (serverTransactions) {
0695: transactionIterator = serverTransactions.elements();
0696: currentTransaction = null;
0697: while (transactionIterator.hasMoreElements()
0698: && currentTransaction == null) {
0699:
0700: nextTransaction = (ServerTransaction) transactionIterator
0701: .nextElement();
0702:
0703: // If this transaction should handle this request,
0704: if (!nextTransaction.isTerminated()
0705: && nextTransaction
0706: .isMessagePartOfTransaction(requestReceived)) {
0707: // Mark this transaction as the one
0708: // to handle this message
0709: currentTransaction = nextTransaction;
0710: }
0711: }
0712:
0713: // If no transaction exists to handle this message
0714: if (currentTransaction == null) {
0715: currentTransaction = createServerTransaction(requestMessageChannel);
0716: currentTransaction
0717: .setOriginalRequest(requestReceived);
0718: if (!isDialogCreated(requestReceived.getMethod())) {
0719: /*
0720: * IMPL_NOTE: remove the following block of code
0721: * after ensuring that it doesn't break anything.
0722: */
0723:
0724: /*
0725: // Dialog is not created - can we find the state?
0726: // If so, then create a transaction and add it.
0727: String dialogId = requestReceived.getDialogId(true);
0728: Dialog dialog = getDialog(dialogId);
0729: // Sequence numbers are supposed to increment.
0730: // avoid processing old sequence numbers and
0731: // delivering the same request up to the
0732: // application if the request has already been seen.
0733: // Special handling applies to ACK processing.
0734: if (dialog != null &&
0735: (requestReceived.getMethod().equals(Request.ACK)
0736: || requestReceived.getCSeqHeader()
0737: .getSequenceNumber() >
0738: dialog.getRemoteSequenceNumber())) {
0739: // Found a dialog.
0740: if (Logging.REPORT_LEVEL <=
0741: Logging.INFORMATION) {
0742: Logging.report(Logging.INFORMATION,
0743: LogChannels.LC_JSR180,
0744: "adding server transaction " +
0745: currentTransaction);
0746: }
0747:
0748: serverTransactions.addElement(currentTransaction);
0749: currentTransaction.isMapped = true;
0750: }
0751: */
0752:
0753: // Server transactions must always be added
0754: // to the corresponding vector.
0755: serverTransactions
0756: .addElement(currentTransaction);
0757: currentTransaction.isMapped = true;
0758: } else {
0759: // Create the transaction but dont map it.
0760: String dialogId = requestReceived
0761: .getDialogId(true);
0762: Dialog dialog = getDialog(dialogId);
0763: // This is a dialog creating request that is
0764: // part of an existing dialog
0765: // (eg. re-Invite). Re-invites get a non null
0766: // server transaction Id (unlike the original
0767: // invite).
0768: if (dialog != null
0769: && requestReceived.getCSeqHeader()
0770: .getSequenceNumber() > dialog
0771: .getRemoteSequenceNumber()) {
0772: try {
0773: currentTransaction.map();
0774: } catch (IOException ex) {
0775: /* Ignore */
0776: }
0777: }
0778:
0779: serverTransactions
0780: .addElement(currentTransaction);
0781: currentTransaction.toListener = true;
0782: }
0783: }
0784: // attach to request
0785: requestReceived.setTransaction(currentTransaction);
0786:
0787: // Set ths transaction's encapsulated request
0788: // interface from the superclass
0789: currentTransaction.setRequestInterface(super
0790: .newSIPServerRequest(requestReceived,
0791: currentTransaction));
0792:
0793: return currentTransaction;
0794: }
0795: } catch (RuntimeException ex) {
0796: ex.printStackTrace();
0797: throw ex;
0798: }
0799:
0800: }
0801:
0802: /**
0803: * Handles a new SIP response.
0804: * It finds a client transaction to handle
0805: * this message. If none exists, it sends the message directly to the
0806: * superclass.
0807: * @param responseReceived Response to handle.
0808: * @param responseMessageChannel Channel that received message.
0809: * @return A client transaction.
0810: */
0811: SIPServerResponseInterface newSIPServerResponse(
0812: Response responseReceived,
0813: MessageChannel responseMessageChannel) {
0814: // System.out.println("response = " + responseReceived.encode());
0815: // Iterator through all client transactions
0816: Enumeration transactionIterator;
0817: // Next transaction in the set
0818: ClientTransaction nextTransaction;
0819: // Transaction to handle this request
0820: ClientTransaction currentTransaction;
0821:
0822: // Loop through all client transactions
0823: synchronized (clientTransactions) {
0824: transactionIterator = clientTransactions.elements();
0825: currentTransaction = null;
0826: int i = -1;
0827: while (transactionIterator.hasMoreElements()
0828: && currentTransaction == null) {
0829: i++;
0830: nextTransaction = (ClientTransaction) transactionIterator
0831: .nextElement();
0832: // If this transaction should handle this request,
0833: if (nextTransaction
0834: .isMessageTransOrMult(responseReceived)) {
0835: if (nextTransaction
0836: .isMultipleResponse(responseReceived)) {
0837: // RFC 3261, 13.2.2.4:
0838: // Multiple 2xx responses may arrive at the UAC for
0839: // a single INVITE request due to a forking proxy.
0840: // create a new client transaction
0841: currentTransaction = nextTransaction
0842: .cloneWithNewLastResponse(responseReceived);
0843: currentTransaction
0844: .setState(Transaction.PROCEEDING_STATE);
0845: currentTransaction
0846: .setApplicationData(nextTransaction
0847: .getApplicationData());
0848: Dialog dialog = new Dialog(currentTransaction);
0849: dialog.setDialogId(responseReceived
0850: .getDialogId(false));
0851: dialog
0852: .setRemoteTag(responseReceived
0853: .getToTag());
0854: dialog.setStack(this );
0855: putDialog(dialog);
0856: currentTransaction.setDialog(dialog);
0857: // change from old to new transaction
0858: clientTransactions.setElementAt(
0859: currentTransaction, i);
0860: } else { // not multiple response
0861: // Mark this transaction as the one to
0862: // handle this message
0863: currentTransaction = nextTransaction;
0864: }
0865:
0866: }
0867: }
0868: }
0869: // If no transaction exists to handle this message,
0870: if (currentTransaction == null) {
0871: // Pass the message directly to the TU
0872: return super .newSIPServerResponse(responseReceived,
0873: responseMessageChannel);
0874: }
0875: // Set ths transaction's encapsulated response interface
0876: // from the superclass
0877: currentTransaction.setResponseInterface(super
0878: .newSIPServerResponse(responseReceived,
0879: currentTransaction));
0880: return currentTransaction;
0881: }
0882:
0883: /**
0884: * Creates a client transaction to handle a new request.
0885: * Gets the real
0886: * message channel from the superclass, and then creates a new client
0887: * transaction wrapped around this channel.
0888: * @param nextHop Hop to create a channel to contact.
0889: * @return the requested message channel
0890: */
0891: public MessageChannel createMessageChannel(Hop nextHop) {
0892: synchronized (clientTransactions) {
0893: // New client transaction to return
0894: Transaction returnChannel;
0895:
0896: // Create a new client transaction around the
0897: // superclass' message channel
0898: MessageChannel mc = super .createMessageChannel(nextHop);
0899: if (mc == null)
0900: return null;
0901: returnChannel = createClientTransaction(mc);
0902: clientTransactions.addElement(returnChannel);
0903: ((ClientTransaction) returnChannel).setViaPort(nextHop
0904: .getPort());
0905: ((ClientTransaction) returnChannel).setViaHost(nextHop
0906: .getHost());
0907: return returnChannel;
0908: }
0909: }
0910:
0911: /**
0912: * Creates a client transaction from a raw channel.
0913: * @param rawChannel is the transport channel to encapsulate.
0914: * @return the requested message channel
0915: */
0916: public MessageChannel createMessageChannel(MessageChannel rawChannel) {
0917: synchronized (clientTransactions) {
0918: // New client transaction to return
0919: Transaction returnChannel = createClientTransaction(rawChannel);
0920: clientTransactions.addElement(returnChannel);
0921: ((ClientTransaction) returnChannel).setViaPort(rawChannel
0922: .getViaPort());
0923: ((ClientTransaction) returnChannel).setViaHost(rawChannel
0924: .getHost());
0925: return returnChannel;
0926: }
0927: }
0928:
0929: /**
0930: * Creates a client transaction from a raw channel.
0931: * @param transaction the requested transaction
0932: * @return the requested message channel
0933: */
0934: public MessageChannel createMessageChannel(Transaction transaction) {
0935: synchronized (clientTransactions) {
0936: // New client transaction to return
0937: Transaction returnChannel = createClientTransaction(transaction
0938: .getMessageChannel());
0939: clientTransactions.addElement(returnChannel);
0940: ((ClientTransaction) returnChannel).setViaPort(transaction
0941: .getViaPort());
0942: ((ClientTransaction) returnChannel).setViaHost(transaction
0943: .getViaHost());
0944: return returnChannel;
0945: }
0946: }
0947:
0948: /**
0949: * Creates a client transaction that encapsulates a MessageChannel.
0950: * Useful for implementations that want to subclass the standard
0951: * @param encapsulatedMessageChannel
0952: * Message channel of the transport layer.
0953: * @return the requested client transaction
0954: */
0955: public ClientTransaction createClientTransaction(
0956: MessageChannel encapsulatedMessageChannel) {
0957:
0958: return new ClientTransaction(this , encapsulatedMessageChannel);
0959:
0960: }
0961:
0962: /**
0963: * Creates a server transaction that encapsulates a MessageChannel.
0964: * Useful for implementations that want to subclass the standard
0965: * @param encapsulatedMessageChannel
0966: * Message channel of the transport layer.
0967: * @return the requested server transaction
0968: */
0969: public ServerTransaction createServerTransaction(
0970: MessageChannel encapsulatedMessageChannel) {
0971:
0972: return new ServerTransaction(this , encapsulatedMessageChannel);
0973: }
0974:
0975: /**
0976: * Creates a raw message channel. A raw message channel has no
0977: * transaction wrapper.
0978: * @param hop hop for which to create the raw message channel.
0979: * @return the requested message channel
0980: */
0981: public MessageChannel createRawMessageChannel(Hop hop) {
0982: return super .createMessageChannel(hop);
0983: }
0984:
0985: /**
0986: * Adds a new client transaction to the set of existing transactions.
0987: * @param clientTransaction -- client transaction to add to the set.
0988: */
0989: public void addTransaction(ClientTransaction clientTransaction) {
0990: synchronized (clientTransactions) {
0991: clientTransactions.addElement(clientTransaction);
0992: }
0993: }
0994:
0995: /**
0996: * Adds a new client transaction to the set of existing transactions.
0997: * @param serverTransaction -- server transaction to add to the set.
0998: */
0999: public void addTransaction(ServerTransaction serverTransaction)
1000: throws IOException {
1001: synchronized (serverTransactions) {
1002: this.serverTransactions.addElement(serverTransaction);
1003: }
1004: }
1005: }
|