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.*;
030: import gov.nist.siplite.address.*;
031: import gov.nist.core.*;
032: import java.util.*;
033:
034: import java.io.IOException;
035: import javax.microedition.sip.SipException;
036:
037: import com.sun.midp.log.Logging;
038: import com.sun.midp.log.LogChannels;
039:
040: /**
041: * Represents a client transaction.
042: *
043: * @version JAIN-SIP-1.1
044: *
045: * <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
046: *
047: * <pre>
048: *
049: * Implements the following state machines. (FromHeader RFC 3261)
050: *
051: * |INVITE from TU
052: * Timer A fires |INVITE sent
053: * Reset A, V Timer B fires
054: * INVITE sent +-----------+ or Transport Err.
055: * +---------| |---------------+inform TU
056: * | | Calling | |
057: * +-------->| |-------------->|
058: * +-----------+ 2xx |
059: * | | 2xx to TU |
060: * | |1xx |
061: * 300-699 +---------------+ |1xx to TU |
062: * ACK sent | | |
063: * resp. to TU | 1xx V |
064: * | 1xx to TU -----------+ |
065: * | +---------| | |
066: * | | |Proceeding |-------------->|
067: * | +-------->| | 2xx |
068: * | +-----------+ 2xx to TU |
069: * | 300-699 | |
070: * | ACK sent, | |
071: * | resp. to TU| |
072: * | | | NOTE:
073: * | 300-699 V |
074: * | ACK sent +-----------+Transport Err. | transitions
075: * | +---------| |Inform TU | labeled with
076: * | | | Completed |-------------->| the event
077: * | +-------->| | | over the action
078: * | +-----------+ | to take
079: * | ^ | |
080: * | | | Timer D fires |
081: * +--------------+ | - |
082: * | |
083: * V |
084: * +-----------+ |
085: * | | |
086: * | Terminated|<--------------+
087: * | |
088: * +-----------+
089: *
090: * Figure 5: INVITE client transaction
091: *
092: *
093: * |Request from TU
094: * |send request
095: * Timer E V
096: * send request +-----------+
097: * +---------| |-------------------+
098: * | | Trying | Timer F |
099: * +-------->| | or Transport Err.|
100: * +-----------+ inform TU |
101: * 200-699 | | |
102: * resp. to TU | |1xx |
103: * +---------------+ |resp. to TU |
104: * | | |
105: * | Timer E V Timer F |
106: * | send req +-----------+ or Transport Err. |
107: * | +---------| | inform TU |
108: * | | |Proceeding |------------------>|
109: * | +-------->| |-----+ |
110: * | +-----------+ |1xx |
111: * | | ^ |resp to TU |
112: * | 200-699 | +--------+ |
113: * | resp. to TU | |
114: * | | |
115: * | V |
116: * | +-----------+ |
117: * | | | |
118: * | | Completed | |
119: * | | | |
120: * | +-----------+ |
121: * | ^ | |
122: * | | | Timer K |
123: * +--------------+ | - |
124: * | |
125: * V |
126: * NOTE: +-----------+ |
127: * | | |
128: * transitions | Terminated|<------------------+
129: * labeled with | |
130: * the event +-----------+
131: * over the action
132: * to take
133: *
134: * Figure 6: non-INVITE client transaction
135: * </pre>
136: */
137: public class ClientTransaction extends Transaction implements
138: SIPServerResponseInterface {
139: /** Last request. */
140: private Request lastRequest;
141: /** Flag indicating events are pending. */
142: private boolean eventPending;
143: /** Via port number. */
144: private int viaPort;
145: /** Via host. */
146: private String viaHost;
147: /** Real ResponseInterface to pass messages to. */
148: private SIPServerResponseInterface respondTo;
149:
150: /**
151: * Creates a new client transaction.
152: *
153: * @param newSIPMessageStack Transaction stack this transaction
154: * belongs to.
155: * @param newChannelToHeaderUse Channel to encapsulate.
156: */
157: protected ClientTransaction(SIPTransactionStack newSIPMessageStack,
158: MessageChannel newChannelToHeaderUse) {
159: super (newSIPMessageStack, newChannelToHeaderUse);
160: setBranch(SIPUtils.generateBranchId());
161:
162: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
163: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
164: "Creating clientTransaction " + this );
165: // new Exception().printStackTrace();
166: }
167: }
168:
169: /**
170: * Sets the real ResponseInterface this transaction encapsulates.
171: *
172: * @param newRespondToHeader ResponseInterface to send messages to.
173: */
174: public void setResponseInterface(
175: SIPServerResponseInterface newRespondToHeader) {
176: respondTo = newRespondToHeader;
177: }
178:
179: /**
180: * Gets the processing information.
181: * @return processing information
182: */
183: public String getProcessingInfo() {
184: return respondTo.getProcessingInfo();
185: }
186:
187: /**
188: * Returns this transaction.
189: * @return request channel transaction
190: */
191: public MessageChannel getRequestChannel() {
192: return this ;
193: }
194:
195: /**
196: * Deterines if the message is a part of this transaction.
197: *
198: * @param messageToHeaderTest Message to check if it is part of this
199: * transaction.
200: *
201: * @return True if the message is part of this transaction,
202: * false if not.
203: */
204: public boolean isMessagePartOfTransaction(
205: Message messageToHeaderTest) {
206: return isMessageTransOrMult(messageToHeaderTest, false);
207: }
208:
209: /**
210: * Deterines if the message is a part of this transaction or it is
211: * multiple 2xx response.
212: *
213: * @param messageToHeaderTest Message to check if it is part of this
214: * transaction.
215: *
216: * @return True if the message is part of this transaction,
217: * false if not.
218: */
219: public boolean isMessageTransOrMult(Message messageToHeaderTest) {
220: return isMessageTransOrMult(messageToHeaderTest, true);
221: }
222:
223: /**
224: * Deterines if the message is a part of this transaction or it is
225: * multiple 2xx response.
226: *
227: * @param messageToHeaderTest Message to check if it is part of this
228: * transaction.
229: * @param checkMultResponse flag of checking multiple 2xx response
230: *
231: * @return True if the message is part of this transaction,
232: * false if not.
233: */
234: private boolean isMessageTransOrMult(Message messageToHeaderTest,
235: boolean checkMultResponse) {
236:
237: // List of Via headers in the message to test
238: ViaList viaHeaders = messageToHeaderTest.getViaHeaders();
239: // Flags whether the select message is part of this transaction
240: boolean transactionMatches;
241: String messageBranch = ((ViaHeader) viaHeaders.getFirst())
242: .getBranch();
243: boolean rfc3261Compliant = (getBranch() != null)
244: && (messageBranch != null)
245: && getBranch().startsWith(
246: SIPConstants.GENERAL_BRANCH_MAGIC_COOKIE)
247: && messageBranch
248: .startsWith(SIPConstants.GENERAL_BRANCH_MAGIC_COOKIE);
249:
250: /**
251: * if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
252: * Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
253: * "--------- TEST ------------");
254: * Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
255: * " testing " + this.getOriginalRequest());
256: * Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
257: * "Against " + messageToHeaderTest);
258: * Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
259: * "isTerminated = " + isTerminated());
260: * Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
261: * "messageBranch = " + messageBranch);
262: * Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
263: * "viaList = " + messageToHeaderTest.getViaHeaders());
264: * Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
265: * "myBranch = " + getBranch());
266: * }
267: */
268:
269: transactionMatches = false;
270: // Response 2xx should be processed even in TERMINATED state
271: // RFC 3261, 13.2.2.4:
272: // Multiple 2xx responses may arrive at the UAC for a single INVITE
273: // request due to a forking proxy.
274: boolean isResponse = messageToHeaderTest instanceof Response;
275: if (!isTerminated()
276: || (checkMultResponse && isTerminated() && isResponse
277: && isInviteTransaction() && (((Response) messageToHeaderTest)
278: .getStatusCode() / 100 == 2))) {
279: if (rfc3261Compliant) {
280: if (viaHeaders != null) {
281: // If the branch parameter is the
282: // same as this transaction and the method is the same,
283: if (getBranch().equals(
284: ((ViaHeader) viaHeaders.getFirst())
285: .getBranch())) {
286: transactionMatches = getOriginalRequest()
287: .getCSeqHeader().getMethod().equals(
288: messageToHeaderTest
289: .getCSeqHeader()
290: .getMethod());
291:
292: }
293: }
294: } else {
295: transactionMatches = getOriginalRequest()
296: .getTransactionId().equals(
297: messageToHeaderTest.getTransactionId());
298:
299: }
300:
301: }
302: return transactionMatches;
303:
304: }
305:
306: /**
307: * Deterines if the response is multiple (RFC 3261, 13.2.2.4).
308: *
309: * @param response response for checking
310: * transaction.
311: *
312: * @return True if the input response has 2xx status, the current
313: * transaction has TERMINATED state and the To tag is not same as
314: * To tag of current transaction. This method doesn't compare other
315: * members, it should be use with method isMessageTransOrMult
316: * together .
317: */
318: public boolean isMultipleResponse(Response response) {
319: boolean returnValue = false;
320: if ((response.getStatusCode() / 100 == 2) && isTerminated()) {
321: Response lastResponse = getLastResponse();
322: if (lastResponse != null) {
323: String newTag = response.getToTag();
324: returnValue = !newTag.equals(lastResponse.getToTag());
325: }
326: }
327: return returnValue;
328: }
329:
330: /**
331: * Sends a request message through this transaction and
332: * onto the client.
333: *
334: * @param messageToHeaderSend Request to process and send.
335: */
336: public void sendMessage(Message messageToHeaderSend)
337: throws IOException {
338:
339: // Message typecast as a request
340: Request transactionRequest;
341:
342: transactionRequest = (Request) messageToHeaderSend;
343:
344: // Set the branch id for the top via header.
345: ViaHeader topVia = (ViaHeader) transactionRequest
346: .getViaHeaders().getFirst();
347: // Tack on a branch identifier to match responses.
348:
349: topVia.setBranch(getBranch());
350:
351: // If this is the first request for this transaction,
352: if (getState() == INITIAL_STATE) {
353: // Save this request as the one this transaction
354: // is handling
355: setOriginalRequest(transactionRequest);
356:
357: // Change to trying/calling state
358: if (transactionRequest.getMethod().equals(Request.INVITE)) {
359: setState(CALLING_STATE);
360: } else if (transactionRequest.getMethod().equals(
361: Request.ACK)) {
362: // Acks are never retransmitted.
363: setState(TERMINATED_STATE);
364: } else {
365: setState(TRYING_STATE);
366: }
367:
368: if (!isReliable()) {
369: enableRetransmissionTimer();
370: }
371:
372: if (isInviteTransaction()) {
373: enableTimeoutTimer(TIMER_B);
374: } else {
375: enableTimeoutTimer(TIMER_F);
376: }
377:
378: } else if (getState() == PROCEEDING_STATE
379: || getState() == CALLING_STATE) {
380:
381: // If this is a TU-generated ACK request,
382: if (transactionRequest.getMethod().equals(Request.ACK)) {
383: // Send directly to the underlying
384: // transport and close this transaction
385: setState(TERMINATED_STATE);
386: getMessageChannel().sendMessage(transactionRequest);
387: return;
388:
389: }
390:
391: }
392:
393: try {
394: // Send the message to the server
395: lastRequest = transactionRequest;
396: getMessageChannel().sendMessage(transactionRequest);
397: } catch (IOException e) {
398: setState(TERMINATED_STATE);
399: throw e;
400: }
401: }
402:
403: /**
404: * Processes a new response message through this transaction.
405: * If necessary, this message will also be passed onto the TU.
406: *
407: * @param transactionResponse Response to process.
408: * @param sourceChannel Channel that received this message.
409: */
410: public synchronized void processResponse(
411: Response transactionResponse, MessageChannel sourceChannel)
412: throws SIPServerException {
413:
414: // Log the incoming response in our log file.
415: if (ServerLog.needsLogging(ServerLog.TRACE_MESSAGES))
416: this .logResponse(transactionResponse, System
417: .currentTimeMillis(), "normal processing");
418:
419: int statusGroup = transactionResponse.getStatusCode() / 100;
420:
421: // Ignore 1xx
422: if (getState() == COMPLETED_STATE && statusGroup == 1) {
423: return;
424: // This block is against RFC 3261 17.1.1.2, figure 5 and
425: // 17.1.4, figure 6
426: /*
427: } else if (PROCEEDING_STATE == this.getState()
428: && transactionResponse.getStatusCode() == 100) {
429: // Ignore 100 if received after 180
430: return;
431: */
432: } else {
433: // IMPL_NOTE: investigate if this flag may be completely removed.
434: while (eventPending) {
435: try {
436: // Wait for clearEventPending() call.
437: wait();
438: } catch (InterruptedException e) {
439: // intentionally ignored
440: // wait for clearEventPending() call
441: }
442: }
443: }
444:
445: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
446: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
447: "processing " + transactionResponse.getFirstLine()
448: + "current state = " + getState());
449: }
450:
451: this .lastResponse = transactionResponse;
452:
453: if ((dialog != null) && (statusGroup < 3)) {
454: // add the route before you process the response.
455: dialog.addRoute(transactionResponse);
456: }
457:
458: String method = transactionResponse.getCSeqHeader().getMethod();
459:
460: if (dialog != null) {
461: boolean added = false;
462: SIPTransactionStack sipStackImpl = (SIPTransactionStack) getSIPStack();
463:
464: // A tag just got assigned or changed.
465: if (dialog.getRemoteTag() == null
466: && transactionResponse.getTo().getTag() != null) {
467:
468: // Dont assign tag on provisional response
469: if (transactionResponse.getStatusCode() != 100) {
470: dialog.setRemoteTag(transactionResponse.getToTag());
471: }
472:
473: String dialogId = transactionResponse
474: .getDialogId(false);
475: dialog.setDialogId(dialogId);
476:
477: if (sipStackImpl.isDialogCreated(method)
478: && transactionResponse.getStatusCode() != 100) {
479: sipStackImpl.putDialog(dialog);
480: if (statusGroup == 1) {
481: dialog.setState(Dialog.EARLY_STATE);
482: } else if (statusGroup == 2) {
483: dialog.setState(Dialog.CONFIRMED_STATE);
484: }
485: added = true;
486: }
487:
488: } else if (dialog.getRemoteTag() != null
489: && transactionResponse.getToTag() != null
490: && !dialog.getRemoteTag().equals(
491: transactionResponse.getToTag())) {
492: dialog.setRemoteTag(transactionResponse.getToTag());
493: String dialogId = transactionResponse
494: .getDialogId(false);
495: dialog.setDialogId(dialogId);
496:
497: if (sipStackImpl.isDialogCreated(method)) {
498: sipStackImpl.putDialog(dialog);
499: added = true;
500: }
501: }
502:
503: if (sipStackImpl.isDialogCreated(method)) {
504: // Make a final tag assignment.
505: if (transactionResponse.getToTag() != null
506: && statusGroup == 2) {
507: // This is a dialog creating method (such as INVITE).
508: // 2xx response -- set the state to the confirmed
509: // state.
510: dialog.setRemoteTag(transactionResponse.getToTag());
511: dialog.setState(Dialog.CONFIRMED_STATE);
512: } else if ((transactionResponse.getStatusCode() == 487
513: || statusGroup == 5 || statusGroup == 6)
514: && (dialog.getState() == -1 || dialog
515: .getState() == Dialog.EARLY_STATE)) {
516: // Invite transaction generated an error.
517: dialog.setState(Dialog.TERMINATED_STATE);
518: }
519: }
520:
521: // 200 OK for a bye so terminate the dialog.
522: if (transactionResponse.getCSeqHeader().getMethod().equals(
523: Request.BYE)
524: && transactionResponse.getStatusCode() == 200) {
525: dialog.setState(Dialog.TERMINATED_STATE);
526: }
527: }
528:
529: try {
530: if (isInviteTransaction()) {
531: inviteClientTransaction(transactionResponse,
532: sourceChannel);
533: } else {
534: nonInviteClientTransaction(transactionResponse,
535: sourceChannel);
536: }
537: } catch (IOException ex) {
538: setState(TERMINATED_STATE);
539: raiseErrorEvent(SIPTransactionErrorEvent.TRANSPORT_ERROR);
540: }
541: }
542:
543: /**
544: * Implements the state machine for invite client transactions.
545: * @param transactionResponse -- transaction response received.
546: * @param sourceChannel - source channel on which the response was received.
547: * <pre>
548: *
549: * |Request from TU
550: * |send request
551: * Timer E V
552: * send request +-----------+
553: * +---------| |-------------------+
554: * | | Trying | Timer F |
555: * +-------->| | or Transport Err.|
556: * +-----------+ inform TU |
557: * 200-699 | | |
558: * resp. to TU | |1xx |
559: * +---------------+ |resp. to TU |
560: * | | |
561: * | Timer E V Timer F |
562: * | send req +-----------+ or Transport Err. |
563: * | +---------| | inform TU |
564: * | | |Proceeding |------------------>|
565: * | +-------->| |-----+ |
566: * | +-----------+ |1xx |
567: * | | ^ |resp to TU |
568: * | 200-699 | +--------+ |
569: * | resp. to TU | |
570: * | | |
571: * | V |
572: * | +-----------+ |
573: * | | | |
574: * | | Completed | |
575: * | | | |
576: * | +-----------+ |
577: * | ^ | |
578: * | | | Timer K |
579: * +--------------+ | - |
580: * | |
581: * V |
582: * NOTE: +-----------+ |
583: * | | |
584: * transitions | Terminated|<------------------+
585: * labeled with | |
586: * the event +-----------+
587: * over the action
588: * to take
589: *
590: * Figure 6: non-INVITE client transaction
591: */
592: private void nonInviteClientTransaction(
593: Response transactionResponse, MessageChannel sourceChannel)
594: throws IOException, SIPServerException {
595: int currentState = getState();
596:
597: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
598: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
599: "nonInviteClientTransaction "
600: + transactionResponse.getFirstLine());
601: Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
602: "currentState = " + currentState);
603: }
604:
605: int statusCode = transactionResponse.getStatusCode();
606: if (currentState == TRYING_STATE) {
607: if (statusCode / 100 == 1) {
608: // Response to TU, RFC 3261, 17.1.4, figure 6
609: respondTo.processResponse(transactionResponse, this );
610: setState(PROCEEDING_STATE);
611: enableRetransmissionTimer(MAXIMUM_RETRANSMISSION_TICK_COUNT);
612: enableTimeoutTimer(TIMER_F);
613: } else if (200 <= statusCode && statusCode <= 699) {
614: // Send the response up to the TU.
615: respondTo.processResponse(transactionResponse, this );
616: if (!isReliable()) {
617: setState(COMPLETED_STATE);
618: enableTimeoutTimer(TIMER_K);
619: } else {
620: setState(TERMINATED_STATE);
621: }
622: }
623: } else if (currentState == PROCEEDING_STATE
624: && 200 <= statusCode && statusCode <= 699) {
625:
626: respondTo.processResponse(transactionResponse, this );
627:
628: disableRetransmissionTimer();
629: disableTimeoutTimer();
630: if (!isReliable()) {
631: setState(COMPLETED_STATE);
632: enableTimeoutTimer(TIMER_K);
633: } else {
634: setState(TERMINATED_STATE);
635: }
636: } else if (currentState == PROCEEDING_STATE
637: && statusCode / 100 == 1) {
638: // Response to TU, RFC 3261, 17.1.4, figure 6
639: respondTo.processResponse(transactionResponse, this );
640: }
641: }
642:
643: /**
644: * Implements the state machine for invite client transactions.
645: * @param transactionResponse -- transaction response received.
646: * @param sourceChannel - source channel on which the response was received.
647: * <pre>
648: *
649: * |INVITE from TU
650: * Timer A fires |INVITE sent
651: * Reset A, V Timer B fires
652: * INVITE sent +-----------+ or Transport Err.
653: * +---------| |---------------+inform TU
654: * | | Calling | |
655: * +-------->| |-------------->|
656: * +-----------+ 2xx |
657: * | | 2xx to TU |
658: * | |1xx |
659: * 300-699 +---------------+ |1xx to TU |
660: * ACK sent | | |
661: * resp. to TU | 1xx V |
662: * | 1xx to TU -----------+ |
663: * | +---------| | |
664: * | | |Proceeding |-------------->|
665: * | +-------->| | 2xx |
666: * | +-----------+ 2xx to TU |
667: * | 300-699 | |
668: * | ACK sent, | |
669: * | resp. to TU| |
670: * | | | NOTE:
671: * | 300-699 V |
672: * | ACK sent +-----------+Transport Err. | transitions
673: * | +---------| |Inform TU | labeled with
674: * | | | Completed |-------------->| the event
675: * | +-------->| | | over the action
676: * | +-----------+ | to take
677: * | ^ | |
678: * | | | Timer D fires |
679: * +--------------+ | - |
680: * | |
681: * V |
682: * +-----------+ |
683: * | | |
684: * | Terminated|<--------------+
685: * | |
686: * +-----------+
687: * </pre>
688: */
689: private void inviteClientTransaction(Response transactionResponse,
690: MessageChannel sourceChannel) throws IOException,
691: SIPServerException {
692: int statusCode = transactionResponse.getStatusCode();
693: int currentState = getState();
694:
695: if (currentState == TERMINATED_STATE) {
696: // Do nothing in the terminated state.
697: return;
698: } else if (currentState == CALLING_STATE) {
699: if (statusCode / 100 == 2) {
700: // 200 responses are always seen by TU.
701: respondTo.processResponse(transactionResponse, this );
702: disableRetransmissionTimer();
703: disableTimeoutTimer();
704: setState(TERMINATED_STATE);
705: } else if (statusCode / 100 == 1) {
706: disableRetransmissionTimer();
707: disableTimeoutTimer();
708: respondTo.processResponse(transactionResponse, this );
709: setState(PROCEEDING_STATE);
710: } else if (300 <= statusCode && statusCode <= 699) {
711: // When in either the "Calling" or "Proceeding" states,
712: // reception of response with status code from 300-699
713: // MUST cause the client transaction to
714: // transition to "Completed".
715: // The client transaction MUST pass the received response up to
716: // the TU, and the client transaction MUST generate an
717: // ACK request.
718:
719: respondTo.processResponse(transactionResponse, this );
720:
721: // Send back an ACK request
722: try {
723: sendMessage((Request) createAck());
724: } catch (SipException ex) {
725: InternalErrorHandler.handleException(ex);
726: }
727: if (!isReliable()) {
728: setState(COMPLETED_STATE);
729: enableTimeoutTimer(TIMER_D);
730: } else {
731: // Proceed immediately to the TERMINATED state.
732: setState(TERMINATED_STATE);
733: }
734: }
735: } else if (currentState == PROCEEDING_STATE) {
736: if (statusCode / 100 == 1) {
737: respondTo.processResponse(transactionResponse, this );
738: } else if (statusCode / 100 == 2) {
739: setState(TERMINATED_STATE);
740: respondTo.processResponse(transactionResponse, this );
741: } else if (300 <= statusCode && statusCode <= 699) {
742: respondTo.processResponse(transactionResponse, this );
743: // Send back an ACK request
744: try {
745: sendMessage((Request) createAck());
746: } catch (SipException ex) {
747: InternalErrorHandler.handleException(ex);
748: }
749: if (!isReliable()) {
750: setState(COMPLETED_STATE);
751: enableTimeoutTimer(TIMER_D);
752: } else {
753: setState(TERMINATED_STATE);
754: }
755: }
756: } else if (currentState == COMPLETED_STATE) {
757: if (300 <= statusCode && statusCode <= 699) {
758: // Send back an ACK request
759: try {
760: sendMessage((Request) createAck());
761: } catch (SipException ex) {
762: InternalErrorHandler.handleException(ex);
763: }
764: }
765:
766: }
767:
768: }
769:
770: /**
771: * Sends specified {@link gov.nist.siplite.message.Request} on a unique
772: * client transaction identifier. This method implies that the application
773: * is functioning as either a User Agent Client or a Stateful proxy, hence
774: * the underlying SipProvider acts statefully.
775: * <p>
776: * JAIN SIP defines a retransmission utility specific to user agent
777: * behaviour and the default retransmission behaviour for each method.
778: * <p>
779: * When an application wishes to send a message, it creates a Request
780: * message passes that Request to this method, this method returns the
781: * cleintTransactionId generated by the SipProvider. The Request message
782: * gets sent via the ListeningPoint that this SipProvider is attached to.
783: * <ul>
784: * <li>User Agent Client - must not send a BYE on a confirmed INVITE until
785: * it has received an ACK for its 2xx response or until the server
786: * transaction times out.
787: * </ul>
788: *
789: * @throws IOException if an I/O error occured
790: * @throws SipException if implementation cannot send request for any
791: * other reason
792: */
793: public void sendRequest() throws IOException, SipException {
794: Request sipRequest = getOriginalRequest();
795: sendMessage(sipRequest);
796: }
797:
798: /**
799: * Called by the transaction stack when a retransmission timer
800: * fires.
801: */
802: protected void fireRetransmissionTimer() {
803: boolean noSend = false;
804: try {
805: // Resend the last request sent
806: // System.out.println("fireRetransmissionTimer ");
807: if (this .getState() == -1) {
808: noSend = true;
809: } else {
810: MessageProcessor mp = this .getMessageProcessor();
811: if (mp == null) {
812: noSend = true;
813: } else if (mp.toExit()) {
814: noSend = true;
815: }
816: }
817: int currentState = this .getState();
818: if (!noSend
819: && (currentState == CALLING_STATE || currentState == TRYING_STATE)) {
820: getMessageChannel().sendMessage(lastRequest);
821: }
822: } catch (IOException e) {
823: raiseErrorEvent(SIPTransactionErrorEvent.TRANSPORT_ERROR);
824: }
825: }
826:
827: /**
828: * Called by the transaction stack when a timeout timer fires.
829: */
830: protected void fireTimeoutTimer() {
831: Dialog dialogImpl = this .getDialog();
832: if (getState() == CALLING_STATE || getState() == TRYING_STATE
833: || getState() == PROCEEDING_STATE) {
834: // Timeout occured. If this is asociated with a transaction
835: // creation then kill the dialog.
836: if (dialogImpl != null) {
837: if (((SIPTransactionStack) getSIPStack())
838: .isDialogCreated(this .getOriginalRequest()
839: .getMethod())) {
840: // terminate the enclosing dialog.
841: dialogImpl.setState(Dialog.TERMINATED_STATE);
842: } else if (getOriginalRequest().getMethod().equals(
843: Request.BYE)) {
844: // Terminate the associated dialog on BYE Timeout.
845: dialogImpl.setState(Dialog.TERMINATED_STATE);
846: }
847: }
848: }
849: if (getState() != COMPLETED_STATE) {
850: raiseErrorEvent(SIPTransactionErrorEvent.TIMEOUT_ERROR);
851: } else {
852: setState(TERMINATED_STATE);
853: }
854: }
855:
856: /**
857: * Creates a new Cancel message from the Request associated with this client
858: * transaction. The CANCEL request, is used to cancel the previous request
859: * sent by this client transaction. Specifically, it asks the UAS to cease
860: * processing the request and to generate an error response to that request.
861: *
862: * @return a cancel request generated from the original request.
863: * @throws SipException if the request cannot be cancelled.
864: */
865: public Request createCancel() throws SipException {
866: Request originalRequest = getOriginalRequest();
867: return originalRequest.createCancelRequest();
868: }
869:
870: /**
871: * Creates an ACK request for this transaction
872: *
873: * @return an ack request generated from the original request.
874: * @throws SipException if transaction is in the wrong state to be acked.
875: */
876: public Request createAck() throws SipException {
877: Request originalRequest = getOriginalRequest();
878: int statusCode = 0;
879:
880: if (originalRequest.getMethod().equals(Request.ACK)) {
881: throw new SipException("Cannot ACK an ACK!",
882: SipException.INVALID_OPERATION);
883: } else if (lastResponse == null) {
884: throw new SipException("bad Transaction state",
885: SipException.INVALID_STATE);
886: } else {
887: statusCode = lastResponse.getStatusCode();
888: if (statusCode < 200) {
889: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
890: Logging.report(Logging.INFORMATION,
891: LogChannels.LC_JSR180, "lastResponse = "
892: + lastResponse);
893: }
894:
895: throw new SipException(
896: "Cannot ACK a provisional response!",
897: SipException.INVALID_OPERATION);
898: }
899: }
900:
901: Request ackRequest = originalRequest
902: .createAckRequest((ToHeader) lastResponse.getTo());
903:
904: // Automatic ACK at transaction layer
905: if (300 <= statusCode && statusCode <= 699) {
906: ViaHeader topmostVia = originalRequest.getTopmostVia();
907: if (topmostVia != null) {
908: ackRequest.setHeader(topmostVia);
909: }
910: return ackRequest;
911: }
912:
913: // Pull the record route headers from the last reesponse.
914: buildRouteSet(ackRequest);
915: return ackRequest;
916:
917: }
918:
919: /**
920: * Sets the port of the recipient.
921: * @param port the new via port
922: */
923: protected void setViaPort(int port) {
924: this .viaPort = port;
925: }
926:
927: /**
928: * Sets the port of the recipient.
929: * @param host the new via host
930: */
931: protected void setViaHost(String host) {
932: this .viaHost = host;
933: }
934:
935: /**
936: * Gets the port of the recipient.
937: * @return the via port
938: */
939: public int getViaPort() {
940: return this .viaPort;
941: }
942:
943: /**
944: * Gets the host of the recipient.
945: * @return the via host
946: */
947: public String getViaHost() {
948: return this .viaHost;
949: }
950:
951: /**
952: * Gets the via header for an outgoing request.
953: * @return the via header reader
954: */
955: public ViaHeader getOutgoingViaHeader() {
956: return this .getMessageProcessor().getViaHeader();
957: }
958:
959: /**
960: * Checks if connection is secure.
961: * @return true if connection is secure.
962: */
963: public boolean isSecure() {
964: return encapsulatedChannel.isSecure();
965: }
966:
967: /**
968: * Clears the event pending flag.
969: */
970: public synchronized void clearEventPending() {
971: eventPending = false;
972: notify();
973: }
974:
975: /**
976: * Sets the event pending flag.
977: */
978: public synchronized void setEventPending() {
979: eventPending = true;
980: }
981:
982: /**
983: * Create a new client transaction based on current.
984: * Field lastResponse is filled by input parameter.
985: * @param lastResponse last response
986: * @return new instance of client transaction.
987: */
988: public ClientTransaction cloneWithNewLastResponse(
989: Response lastResponse) {
990: ClientTransaction clientTransaction = new ClientTransaction(
991: (SIPTransactionStack) getSIPStack(),
992: getMessageChannel());
993: clientTransaction.lastResponse = lastResponse;
994: clientTransaction.setOriginalRequest(getOriginalRequest());
995: return clientTransaction;
996: }
997:
998: }
|