001: /*
002: * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights
003: * Reserved. Use is subject to license terms.
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License version
008: * 2 only, as published by the Free Software Foundation.
009: *
010: * This program is distributed in the hope that it will be useful, but
011: * WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * General Public License version 2 for more details (a copy is
014: * included at /legal/license.txt).
015: *
016: * You should have received a copy of the GNU General Public License
017: * version 2 along with this work; if not, write to the Free Software
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
022: * Clara, CA 95054 or visit www.sun.com if you need additional
023: * information or have any questions.
024: */
025: package gov.nist.siplite.stack;
026:
027: import gov.nist.siplite.message.*;
028: import gov.nist.siplite.header.*;
029: import gov.nist.siplite.address.*;
030: import gov.nist.core.*;
031: import gov.nist.siplite.*;
032:
033: import java.io.IOException;
034: import javax.microedition.sip.SipException;
035:
036: import com.sun.midp.log.Logging;
037: import com.sun.midp.log.LogChannels;
038:
039: /**
040: * Represents a server transaction.
041: *
042: *
043: * @version JAIN-SIP-1.1
044: * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
045: */
046: public class ServerTransaction extends Transaction implements
047: SIPServerRequestInterface {
048: /** Collection time. */
049: protected int collectionTime;
050: /** Real RequestInterface to pass messages to. */
051: private SIPServerRequestInterface requestOf;
052: /** Flag indicating this transaction is known to the stack. */
053: protected boolean isMapped;
054:
055: /**
056: * Sends the SIP response.
057: * @param transactionResponse the transaction response
058: * @exception IOException if the response could not be sent
059: */
060: private void sendSIPResponse(Response transactionResponse)
061: throws IOException {
062: if (transactionResponse.getTopmostVia().getParameter(
063: ViaHeader.RECEIVED) == null) {
064: // Send the response back on the same peer
065: // as received.
066: getMessageChannel().sendMessage(transactionResponse);
067: } else {
068: // Respond to the host name in the received parameter.
069: ViaHeader via = transactionResponse.getTopmostVia();
070: String host = via.getParameter(ViaHeader.RECEIVED);
071: int port = via.getPort();
072: if (port == -1)
073: port = 5060;
074: String transport = via.getTransport();
075: Hop hop = new Hop(host + ":" + port + "/" + transport);
076: MessageChannel messageChannel = ((SIPTransactionStack) getSIPStack())
077: .createRawMessageChannel(hop);
078: messageChannel.sendMessage(transactionResponse);
079: }
080: this .lastResponse = transactionResponse;
081: }
082:
083: /**
084: * Delays the sending of the Trying state.
085: */
086: class SendTrying extends Thread {
087: /** Current server transaction. */
088: ServerTransaction myTransaction;
089:
090: /**
091: * Constructore with initial transaction.
092: * @param transaction the transaction to be sent
093: */
094: public SendTrying(ServerTransaction transaction) {
095: myTransaction = transaction;
096: Thread myThread = new Thread(this );
097: myThread.start();
098: }
099:
100: /** Main loop for sending transaction asynchronously. */
101: public void run() {
102: try {
103: Thread.sleep(200);
104: } catch (InterruptedException ex) {
105: }
106:
107: if (myTransaction.getState() == TRYING_STATE) {
108: try {
109: myTransaction.sendMessage(myTransaction
110: .getOriginalRequest().createResponse(100,
111: "Trying"));
112: } catch (IOException ex) {
113: }
114: }
115: return;
116: }
117: }
118:
119: /**
120: * Creates a new server transaction.
121: *
122: * @param newSIPMessageStack Transaction stack this transaction
123: * belongs to.
124: * @param newChannelToHeaderUse Channel to encapsulate.
125: */
126: protected ServerTransaction(SIPTransactionStack newSIPMessageStack,
127: MessageChannel newChannelToHeaderUse) {
128: super (newSIPMessageStack, newChannelToHeaderUse);
129:
130: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
131: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
132: "Creating Server Transaction" + this );
133: // new Exception().printStackTrace();
134: }
135: }
136:
137: /**
138: * Sets the real RequestInterface this transaction encapsulates.
139: *
140: * @param newRequestOf RequestInterface to send messages to.
141: */
142: public void setRequestInterface(
143: SIPServerRequestInterface newRequestOf) {
144: requestOf = newRequestOf;
145: }
146:
147: /**
148: * Gets the processing infromation.
149: * @return the processing information
150: */
151: public String getProcessingInfo() {
152: return requestOf.getProcessingInfo();
153: }
154:
155: /**
156: * Deterines if the message is a part of this transaction.
157: *
158: * @param messageToHeaderTest Message to check if it is part of this
159: * transaction.
160: *
161: * @return True if the message is part of this transaction,
162: * false if not.
163: */
164: public boolean isMessagePartOfTransaction(
165: Message messageToHeaderTest) {
166: // List of Via headers in the message to test
167: ViaList viaHeaders;
168: // ToHeaderpmost Via header in the list
169: ViaHeader topViaHeader;
170: // Branch code in the topmost Via header
171: String messageBranch;
172: // Flags whether the select message is part of this transaction
173: boolean transactionMatches;
174:
175: transactionMatches = false;
176: // Compensation for retransmits after OK has been dispatched
177: // as suggested by Antonis Karydas.
178: if ((((SIPTransactionStack) getSIPStack())
179: .isDialogCreated(((Request) messageToHeaderTest)
180: .getMethod()))
181: || !isTerminated()) {
182: // Get the topmost Via header and its branch parameter
183: viaHeaders = messageToHeaderTest.getViaHeaders();
184: if (viaHeaders != null) {
185: topViaHeader = (ViaHeader) viaHeaders.getFirst();
186: messageBranch = topViaHeader.getBranch();
187: if (messageBranch != null) {
188: // If the branch parameter exists but
189: // does not start with the magic cookie,
190: if (!messageBranch.toUpperCase().startsWith(
191: SIPConstants.GENERAL_BRANCH_MAGIC_COOKIE
192: .toUpperCase())) {
193: // Flags this as old
194: // (RFC2543-compatible) client
195: // version
196: messageBranch = null;
197: }
198: }
199: // If a new branch parameter exists,
200: if (messageBranch != null && this .getBranch() != null) {
201: if (getBranch().equals(messageBranch)
202: && topViaHeader
203: .getSentBy()
204: .equals(
205: ((ViaHeader) getOriginalRequest()
206: .getViaHeaders()
207: .getFirst())
208: .getSentBy())) {
209: // Matching server side transaction with only the
210: // branch parameter.
211: transactionMatches = true;
212: }
213: // If this is an RFC2543-compliant message,
214: } else {
215:
216: // If RequestURI, ToHeader tag, FromHeader tag,
217: // CallIdHeader, CSeqHeader number, and top Via
218: // headers are the same,
219: String originalFromHeaderTag = getOriginalRequest()
220: .getFromHeader().getTag();
221: String this FromHeaderTag = messageToHeaderTest
222: .getFromHeader().getTag();
223: boolean skipFromHeader = (originalFromHeaderTag == null || this FromHeaderTag == null);
224: String originalToHeaderTag = getOriginalRequest()
225: .getTo().getTag();
226: String this ToHeaderTag = messageToHeaderTest
227: .getTo().getTag();
228: boolean skipToHeader = (originalToHeaderTag == null || this ToHeaderTag == null);
229: if (getOriginalRequest().getRequestURI().equals(
230: ((Request) messageToHeaderTest)
231: .getRequestURI())
232: && (skipFromHeader || originalFromHeaderTag
233: .equals(this FromHeaderTag))
234: && (skipToHeader || originalToHeaderTag
235: .equals(this ToHeaderTag))
236: && getOriginalRequest().getCallId()
237: .getCallId().equals(
238: messageToHeaderTest
239: .getCallId()
240: .getCallId())
241: && getOriginalRequest().getCSeqHeader()
242: .getSequenceNumber() == messageToHeaderTest
243: .getCSeqHeader()
244: .getSequenceNumber()
245: && topViaHeader.equals(getOriginalRequest()
246: .getViaHeaders().getFirst())) {
247: transactionMatches = true;
248: }
249: }
250: }
251: }
252:
253: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
254: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
255: "TRANSACTION MATCHES:" + transactionMatches);
256: }
257:
258: return transactionMatches;
259: }
260:
261: /**
262: * Sends out a trying response (only happens when the transaction is
263: * mapped). Otherwise the transaction is not known to the stack.
264: * @exception IOException if the attempt to send fails
265: */
266: protected void map() throws IOException {
267: if (getState() == -1 || getState() == TRYING_STATE) {
268: if (isInviteTransaction() && !this .isMapped) {
269: this .isMapped = true;
270: // Has side-effect of setting
271: // state to "Proceeding"
272: new SendTrying(this );
273: } else {
274: isMapped = true;
275: }
276: }
277: }
278:
279: /**
280: * Returns true if the transaction is known to stack.
281: * @return true if the transaction is already mapped
282: */
283: public boolean isTransactionMapped() {
284: return this .isMapped;
285: }
286:
287: /**
288: * Processes a new request message through this transaction.
289: * If necessary, this message will also be passed onto the TU.
290: *
291: * IMPL_NOTE:
292: * Receiving the PUBLISH request at ESC (i.e. UAS):
293: * The event state is identified by three major pieces:
294: * Request-URI, event-type and entity-tag (RFC3903, section 4.1).
295: * Maybe it is needed to maintain the event state vector in our
296: * implementation.
297: *
298: * @param transactionRequest Request to process.
299: * @param sourceChannel Channel that received this message.
300: */
301: public void processRequest(Request transactionRequest,
302: MessageChannel sourceChannel) throws SIPServerException {
303: boolean toTu = false;
304:
305: try {
306: // If this is the first request for this transaction,
307: if (getState() == -1) {
308: // Save this request as the one this
309: // transaction is handling
310: setOriginalRequest(transactionRequest);
311: setState(TRYING_STATE);
312: toTu = true;
313: if (isInviteTransaction() && this .isMapped) {
314: // Has side-effect of setting
315: // state to "Proceeding".
316: sendMessage(transactionRequest.createResponse(100,
317: "Trying"));
318: }
319: // If an invite transaction is ACK'ed while in
320: // the completed state,
321: } else if (isInviteTransaction()
322: && COMPLETED_STATE == getState()
323: && transactionRequest.getMethod().equals(
324: Request.ACK)) {
325: setState(CONFIRMED_STATE);
326: disableRetransmissionTimer();
327: if (!isReliable()) {
328: if (this .lastResponse != null
329: && this .lastResponse.getStatusCode() == Response.REQUEST_TERMINATED) {
330: setState(TERMINATED_STATE);
331: } else {
332: enableTimeoutTimer(TIMER_I);
333: }
334: } else {
335: setState(TERMINATED_STATE);
336: }
337: // Application should not Ack in CONFIRMED state
338: return;
339: } else if (transactionRequest.getMethod().equals(
340: getOriginalRequest().getMethod())) {
341: if (getState() == PROCEEDING_STATE
342: || getState() == COMPLETED_STATE) {
343: // Resend the last response to
344: // the client
345: if (lastResponse != null) {
346: try {
347: // Send the message to the client
348: getMessageChannel().sendMessage(
349: lastResponse);
350: } catch (IOException e) {
351: setState(TERMINATED_STATE);
352: throw e;
353: }
354: }
355: } else if (transactionRequest.getMethod().equals(
356: Request.ACK)) {
357: // This is passed up to the TU to suppress
358: // retransmission of OK
359: requestOf.processRequest(transactionRequest, this );
360: }
361: return;
362: }
363:
364: // Pass message to the TU
365: if (COMPLETED_STATE != getState()
366: && TERMINATED_STATE != getState()
367: && requestOf != null) {
368: if (getOriginalRequest().getMethod().equals(
369: transactionRequest.getMethod())) {
370: // Only send original request to TU once!
371: if (toTu)
372: requestOf.processRequest(transactionRequest,
373: this );
374: } else {
375: requestOf.processRequest(transactionRequest, this );
376: }
377: } else {
378: // need revisit
379: // I am allowing it through!
380: if (((SIPTransactionStack) getSIPStack())
381: .isDialogCreated(getOriginalRequest()
382: .getMethod())
383: && getState() == TERMINATED_STATE
384: && transactionRequest.getMethod().equals(
385: Request.ACK) && requestOf != null) {
386: if (!this .getDialog().ackSeen) {
387: (this .getDialog())
388: .ackReceived(transactionRequest);
389: requestOf.processRequest(transactionRequest,
390: this );
391: }
392: } else if (transactionRequest.getMethod().equals(
393: Request.CANCEL)) {
394:
395: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
396: Logging.report(Logging.INFORMATION,
397: LogChannels.LC_JSR180,
398: "Too late to cancel Transaction");
399: }
400: }
401:
402: // send OK and just ignore the CANCEL.
403: try {
404: this .sendMessage(transactionRequest
405: .createResponse(Response.OK));
406: } catch (IOException ex) {
407: // Transaction is already terminated
408: // just ignore the IOException.
409: }
410:
411: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
412: Logging.report(Logging.INFORMATION,
413: LogChannels.LC_JSR180, "Dropping request "
414: + getState());
415: }
416: }
417: } catch (IOException e) {
418: raiseErrorEvent(SIPTransactionErrorEvent.TRANSPORT_ERROR);
419: }
420: }
421:
422: /**
423: * Sends a response message through this transactionand onto
424: * the client.
425: *
426: * @param messageToSend Response to process and send.
427: */
428: public void sendMessage(Message messageToSend) throws IOException {
429:
430: // Message typecast as a response
431: Response transactionResponse;
432: // Status code of the response being sent to the client
433: int statusCode;
434:
435: // Get the status code from the response
436: transactionResponse = (Response) messageToSend;
437: statusCode = transactionResponse.getStatusCode();
438: Dialog dialog = this .dialog;
439: // super.checkCancel(transactionResponse);
440: // Provided we have set the banch id for this we set the BID for the
441: // outgoing via.
442: if (this .getBranch() != null) {
443: transactionResponse.getTopmostVia().setBranch(
444: this .getBranch());
445: } else {
446: transactionResponse.getTopmostVia().removeParameter(
447: ViaHeader.BRANCH);
448: }
449: // Method of the response does not match the request used to
450: // create the transaction - transaction state does not change.
451: if (!transactionResponse.getCSeqHeader().getMethod().equals(
452: getOriginalRequest().getMethod())) {
453: sendSIPResponse(transactionResponse);
454: return;
455: }
456: if (this .dialog != null) {
457: if (this .dialog.getRemoteTag() == null
458: && transactionResponse.getTo().getTag() != null
459: && ((SIPTransactionStack) this .getSIPStack())
460: .isDialogCreated(transactionResponse
461: .getCSeqHeader().getMethod())) {
462: this .dialog.setRemoteTag(transactionResponse.getTo()
463: .getTag());
464: ((SIPTransactionStack) this .getSIPStack())
465: .putDialog(this .dialog);
466: if (statusCode / 100 == 1)
467: this .dialog.setState(Dialog.EARLY_STATE);
468: } else if (((SIPTransactionStack) this .getSIPStack())
469: .isDialogCreated(transactionResponse
470: .getCSeqHeader().getMethod())) {
471: if (statusCode / 100 == 2) {
472: if (!this .isInviteTransaction()) {
473: this .dialog.setState(Dialog.CONFIRMED_STATE);
474: } else {
475: if (this .dialog.getState() == -1)
476: this .dialog.setState(Dialog.EARLY_STATE);
477: }
478: } else if (statusCode >= 300
479: && statusCode <= 699
480: && (this .dialog.getState() == -1 || this .dialog
481: .getState() == Dialog.EARLY_STATE)) {
482: this .dialog.setState(Dialog.TERMINATED_STATE);
483: }
484: } else if (transactionResponse.getCSeqHeader().getMethod()
485: .equals(Request.BYE)
486: && statusCode / 100 == 2) {
487: // Dialog will be terminated when the transction is terminated.
488: if (!isReliable())
489: this .dialog.setState(Dialog.COMPLETED_STATE);
490: else
491: this .dialog.setState(Dialog.TERMINATED_STATE);
492: }
493: }
494: // If the TU sends a provisional response while in the
495: // trying state,
496: if (getState() == TRYING_STATE) {
497: if (statusCode / 100 == 1) {
498: setState(PROCEEDING_STATE);
499: } else if (200 <= statusCode && statusCode <= 699) {
500: if (!isInviteTransaction()) {
501: setState(COMPLETED_STATE);
502: } else {
503: if (statusCode / 100 == 2) {
504: this .collectionTime = TIMER_J;
505: setState(TERMINATED_STATE);
506: } else
507: setState(COMPLETED_STATE);
508: }
509: if (!isReliable()) {
510: enableRetransmissionTimer();
511: }
512: enableTimeoutTimer(TIMER_J);
513: }
514: // If the transaction is in the proceeding state,
515: } else if (getState() == PROCEEDING_STATE) {
516: if (isInviteTransaction()) {
517: // If the response is a failure message,
518: if (statusCode / 100 == 2) {
519: // Set up to catch returning ACKs
520: // Antonis Karydas: Suggestion
521: // Recall that the CANCEL's response will go
522: // through this transaction
523: // and this may well be it. Do NOT change the
524: // transaction state if this
525: // is a response for a CANCEL.
526: // Wait, instead for the 487 from TU.
527: if (!transactionResponse.getCSeqHeader()
528: .getMethod().equals(Request.CANCEL)) {
529: setState(TERMINATED_STATE);
530: if (!isReliable()) {
531: ((Dialog) this .getDialog())
532: .setRetransmissionTicks();
533: enableRetransmissionTimer();
534:
535: }
536: this .collectionTime = TIMER_J;
537: enableTimeoutTimer(TIMER_J);
538: }
539: } else if (300 <= statusCode && statusCode <= 699) {
540: // Set up to catch returning ACKs
541: setState(COMPLETED_STATE);
542: if (!isReliable()) {
543: enableRetransmissionTimer();
544: }
545: // Changed to TIMER_H as suggested by
546: // Antonis Karydas
547: enableTimeoutTimer(TIMER_H);
548: // If the response is a success message,
549: } else if (statusCode / 100 == 2) {
550: // Terminate the transaction
551: setState(TERMINATED_STATE);
552: disableRetransmissionTimer();
553: disableTimeoutTimer();
554: }
555: // If the transaction is not an invite transaction
556: // and this is a final response,
557: } else if (200 <= statusCode && statusCode <= 699) {
558: // Set up to retransmit this response,
559: // or terminate the transaction
560: setState(COMPLETED_STATE);
561: if (!isReliable()) {
562: disableRetransmissionTimer();
563: enableTimeoutTimer(TIMER_J);
564: } else {
565: setState(TERMINATED_STATE);
566: }
567: }
568:
569: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
570: Logging.report(Logging.INFORMATION,
571: LogChannels.LC_JSR180,
572: "SEND MESSAGE :: SERVER TRANSACTION STATE SET "
573: + getState());
574: }
575:
576: // If the transaction has already completed,
577: } else if (getState() == COMPLETED_STATE) {
578: return;
579: }
580:
581: try {
582: // Send the message to the client
583: lastResponse = transactionResponse;
584: sendSIPResponse(transactionResponse);
585: } catch (IOException e) {
586: setState(TERMINATED_STATE);
587: throw e;
588: }
589: }
590:
591: /**
592: * Gets the via host name.
593: * @return the via host
594: */
595: public String getViaHost() {
596: return encapsulatedChannel.getViaHost();
597: }
598:
599: /**
600: * Gets the via port number.
601: * @return the via port number
602: */
603: public int getViaPort() {
604: return encapsulatedChannel.getViaPort();
605: }
606:
607: /**
608: * Called by the transaction stack when a retransmission
609: * timer fires. This retransmits the last response when the
610: * retransmission filter is enabled.
611: */
612: protected void fireRetransmissionTimer() {
613: try {
614: // Resend the last response sent by this transaction
615: if (isInviteTransaction()
616: && ((SIPTransactionStack) getSIPStack()).retransmissionFilter)
617: getMessageChannel().sendMessage(lastResponse);
618: } catch (IOException e) {
619: raiseErrorEvent(SIPTransactionErrorEvent.TRANSPORT_ERROR);
620: }
621: }
622:
623: /**
624: * Called by the transaction stack when a timeout timer fires.
625: */
626: protected void fireTimeoutTimer() {
627: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
628: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
629: "ServerTransaction.fireTimeoutTimer "
630: + this .getState() + " method = "
631: + this .getOriginalRequest().getMethod());
632: }
633:
634: Dialog dialog = (Dialog) this .getDialog();
635: int mystate = this .getState();
636:
637: if (((SIPTransactionStack) getSIPStack()).isDialogCreated(this
638: .getOriginalRequest().getMethod())
639: && (mystate == super .CALLING_STATE || mystate == super .TRYING_STATE)) {
640: dialog.setState(Dialog.TERMINATED_STATE);
641: } else if (getOriginalRequest().getMethod().equals(Request.BYE)) {
642: if (dialog != null)
643: dialog.setState(Dialog.TERMINATED_STATE);
644: }
645:
646: if ((getState() == CONFIRMED_STATE || getState() == COMPLETED_STATE)
647: && isInviteTransaction()) {
648: raiseErrorEvent(SIPTransactionErrorEvent.TIMEOUT_ERROR);
649: setState(TERMINATED_STATE);
650: } else if (!isInviteTransaction()
651: && (getState() == COMPLETED_STATE || getState() == CONFIRMED_STATE)) {
652: setState(TERMINATED_STATE);
653: } else if (isInviteTransaction()
654: && getState() == TERMINATED_STATE) {
655: // This state could be reached when retransmitting
656: raiseErrorEvent(SIPTransactionErrorEvent.TIMEOUT_ERROR);
657: if (dialog != null)
658: dialog.setState(Dialog.TERMINATED_STATE);
659: }
660: }
661:
662: /**
663: * Gets the last response.
664: * @return the last response
665: */
666: public Response getLastResponse() {
667: return this .lastResponse;
668: }
669:
670: /**
671: * Sets the original request.
672: * @param originalRequest original request to remember
673: */
674: public void setOriginalRequest(Request originalRequest) {
675: super .setOriginalRequest(originalRequest);
676: // ACK Server Transaction is just a dummy transaction.
677: if (originalRequest.getMethod().equals("ACK"))
678: this .setState(TERMINATED_STATE);
679:
680: }
681:
682: /**
683: * Sends specified Response message to a Request which is identified by the
684: * specified server transaction identifier. The semantics for various
685: * application behaviour on sending Responses to Requests is outlined at
686: * {@link SipListener#processRequest(RequestEvent)}.
687: * <p>
688: * Note that when a UAS core sends a 2xx response to an INVITE, the server
689: * transaction is destroyed, by the underlying JAIN SIP implementation.
690: * This means that when the ACK sent by the corresponding UAC arrives
691: * at the UAS, there will be no matching server transaction for the ACK,
692: * and based on this rule, the ACK is passed to the UAS application core,
693: * where it is processed.
694: * This ensures that the three way handsake of an INVITE that is managed by
695: * the UAS application and not JAIN SIP.
696: *
697: * @param response the Response to send to the Request
698: * @throws IOException if an I/O error occured
699: * @throws SipException if implementation cannot send response for any
700: * other reason
701: * @see Response
702: */
703: public void sendResponse(Response response) throws IOException,
704: SipException {
705: try {
706: Dialog dialog = (Dialog) getDialog();
707: // Fix up the response if the dialog has already been established.
708: Response responseImpl = response;
709: int statusCode = responseImpl.getStatusCode();
710: int statusGroup = statusCode / 100;
711: if (statusGroup == 2
712: && parentStack.isDialogCreated(responseImpl
713: .getCSeqHeader().getMethod())
714: && dialog != null && dialog.getLocalTag() == null
715: && responseImpl.getTo().getTag() == null) {
716: throw new SipException(
717: "ToHeader tag must be set for OK",
718: SipException.INVALID_MESSAGE);
719: }
720:
721: if (statusGroup == 2
722: && responseImpl.getCSeqHeader().getMethod().equals(
723: Request.INVITE)
724: && responseImpl.getHeader(Header.CONTACT) == null) {
725: throw new SipException(
726: "Contact Header is mandatory for the OK",
727: SipException.INVALID_MESSAGE);
728: }
729:
730: // If sending the response within an established dialog, then
731: // set up the tags appropriately.
732: if (dialog != null && dialog.getLocalTag() != null) {
733: responseImpl.getTo().setTag(dialog.getLocalTag());
734: }
735:
736: String fromTag = getRequest().getFromHeader().getTag();
737:
738: // Backward compatibility slippery slope....
739: // Only set the from tag in the response when the
740: // incoming request has a from tag.
741: if (fromTag != null) {
742: responseImpl.getFromHeader().setTag(fromTag);
743: } else {
744: if (Logging.REPORT_LEVEL <= Logging.WARNING) {
745: Logging
746: .report(Logging.WARNING,
747: LogChannels.LC_JSR180,
748: "WARNING -- Null From tag Dialog layer in jeopardy!!");
749: }
750: }
751:
752: sendMessage(response);
753:
754: // Transaction successfully cancelled but dialog has not yet
755: // been established so delete the dialog.
756: if (Utils.equalsIgnoreCase(responseImpl.getCSeqHeader()
757: .getMethod(), Request.CANCEL)
758: && statusGroup == 2
759: // && (!dialog.isReInvite())
760: && parentStack.isDialogCreated(getOriginalRequest()
761: .getMethod())
762: && (dialog.getState() == Dialog.INITIAL_STATE || dialog
763: .getState() == Dialog.EARLY_STATE)) {
764: dialog.setState(Dialog.TERMINATED_STATE);
765: }
766: // See if the dialog needs to be inserted into the dialog table
767: // or if the state of the dialog needs to be changed.
768: if (dialog != null) {
769: dialog.printTags();
770: if (Utils.equalsIgnoreCase(responseImpl.getCSeqHeader()
771: .getMethod(), Request.BYE)) {
772: dialog.setState(Dialog.TERMINATED_STATE);
773: } else if (Utils.equalsIgnoreCase(responseImpl
774: .getCSeqHeader().getMethod(), Request.CANCEL)) {
775: if (dialog.getState() == -1
776: || dialog.getState() == Dialog.EARLY_STATE) {
777: dialog.setState(Dialog.TERMINATED_STATE);
778: }
779: } else {
780: if (dialog.getLocalTag() == null
781: && responseImpl.getTo().getTag() != null) {
782: if (statusCode != 100)
783: dialog.setLocalTag(responseImpl.getTo()
784: .getTag());
785: }
786: if (parentStack.isDialogCreated(responseImpl
787: .getCSeqHeader().getMethod())) {
788: if (statusGroup == 1 && statusCode != 100) {
789: dialog.setState(Dialog.EARLY_STATE);
790: } else if (statusGroup == 2) {
791: dialog.setState(Dialog.CONFIRMED_STATE);
792: }
793: // Enter into our dialog table provided this is a
794: // dialog creating method.
795: if (statusCode != 100)
796: parentStack.putDialog(dialog);
797: }
798: }
799: }
800: } catch (NullPointerException npe) {
801: if (Logging.REPORT_LEVEL <= Logging.ERROR) {
802: Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
803: "ServerTransaction.sendResponse(): NPE occured: "
804: + npe);
805: npe.printStackTrace();
806: }
807:
808: throw new SipException("NPE occured: " + npe.getMessage(),
809: SipException.GENERAL_ERROR);
810: }
811: }
812:
813: /**
814: * Returns this transaction.
815: * @return the response message channel
816: */
817: public MessageChannel getResponseChannel() {
818: return this;
819: }
820: }
|