1600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang/* 2600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Conditions Of Use 3600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 4600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * This software was developed by employees of the National Institute of 5600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Standards and Technology (NIST), an agency of the Federal Government. 6600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Pursuant to title 15 Untied States Code Section 105, works of NIST 7600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * employees are not subject to copyright protection in the United States 8600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * and are considered to be in the public domain. As a result, a formal 9600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * license is not needed to use the software. 10600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 11600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * This software is provided by NIST as a service and is expressly 12600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED 13600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF 14600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT 15600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * AND DATA ACCURACY. NIST does not warrant or make any representations 16600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * regarding the use of the software or the results thereof, including but 17600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * not limited to the correctness, accuracy, reliability or usefulness of 18600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * the software. 19600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 20600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Permission to use this software is contingent upon your acceptance 21600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * of the terms of this agreement 22600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 23600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * . 24600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 25600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 26600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangpackage gov.nist.javax.sip.stack; 27600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 28600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.core.InternalErrorHandler; 29600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.core.NameValueList; 30600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.SIPConstants; 31600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.Utils; 32600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.address.AddressImpl; 33600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.header.Contact; 34600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.header.RecordRoute; 35600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.header.RecordRouteList; 36600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.header.Route; 37600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.header.RouteList; 38600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.header.TimeStamp; 39600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.header.To; 40600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.header.Via; 41600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.header.ViaList; 42600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.message.SIPMessage; 43600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.message.SIPRequest; 44600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport gov.nist.javax.sip.message.SIPResponse; 45600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 46600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport java.io.IOException; 47600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport java.security.cert.X509Certificate; 48600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport java.text.ParseException; 49600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport java.util.ListIterator; 50600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport java.util.TimerTask; 51600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport java.util.concurrent.ConcurrentHashMap; 52600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 53600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.net.ssl.SSLPeerUnverifiedException; 54600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.Dialog; 55600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.DialogState; 56600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.InvalidArgumentException; 57600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.ObjectInUseException; 58600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.SipException; 59600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.Timeout; 60600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.TimeoutEvent; 61600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.TransactionState; 62600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.address.Hop; 63600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.address.SipURI; 64600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.header.ExpiresHeader; 65600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.header.RouteHeader; 66600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.header.TimeStampHeader; 67600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangimport javax.sip.message.Request; 68600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 69600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang/* 70600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Jeff Keyser -- initial. Daniel J. Martinez Manzano --Added support for TLS message channel. 71600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Emil Ivov -- bug fixes. Chris Beardshear -- bug fix. Andreas Bystrom -- bug fixes. Matt Keller 72600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * (Motorolla) -- bug fix. 73600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 74600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 75600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang/** 76600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Represents a client transaction. Implements the following state machines. (From RFC 3261) 77600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 78600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * <pre> 79600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 80600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 81600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 82600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 83600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 84600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 85600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * |INVITE from TU 86600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Timer A fires |INVITE sent 87600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Reset A, V Timer B fires 88600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * INVITE sent +-----------+ or Transport Err. 89600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +---------| |---------------+inform TU 90600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | Calling | | 91600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +-------->| |-------------->| 92600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +-----------+ 2xx | 93600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | 2xx to TU | 94600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | |1xx | 95600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 300-699 +---------------+ |1xx to TU | 96600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * ACK sent | | | 97600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * resp. to TU | 1xx V | 98600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | 1xx to TU -----------+ | 99600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +---------| | | 100600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | |Proceeding |-------------->| 101600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-------->| | 2xx | 102600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-----------+ 2xx to TU | 103600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | 300-699 | | 104600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | ACK sent, | | 105600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | resp. to TU| | 106600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | NOTE: 107600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | 300-699 V | 108600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | ACK sent +-----------+Transport Err. | transitions 109600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +---------| |Inform TU | labeled with 110600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | Completed |-------------->| the event 111600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-------->| | | over the action 112600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-----------+ | to take 113600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | ˆ | | 114600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | Timer D fires | 115600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +--------------+ | - | 116600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | 117600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * V | 118600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +-----------+ | 119600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | 120600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | Terminated|<--------------+ 121600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | 122600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +-----------+ 123600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 124600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Figure 5: INVITE client transaction 125600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 126600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 127600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * |Request from TU 128600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * |send request 129600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Timer E V 130600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * send request +-----------+ 131600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +---------| |-------------------+ 132600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | Trying | Timer F | 133600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +-------->| | or Transport Err.| 134600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +-----------+ inform TU | 135600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 200-699 | | | 136600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * resp. to TU | |1xx | 137600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +---------------+ |resp. to TU | 138600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | 139600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | Timer E V Timer F | 140600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | send req +-----------+ or Transport Err. | 141600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +---------| | inform TU | 142600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | |Proceeding |------------------>| 143600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-------->| |-----+ | 144600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-----------+ |1xx | 145600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | ˆ |resp to TU | 146600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | 200-699 | +--------+ | 147600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | resp. to TU | | 148600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | 149600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | V | 150600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-----------+ | 151600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | | 152600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | Completed | | 153600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | | 154600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-----------+ | 155600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | ˆ | | 156600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | Timer K | 157600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +--------------+ | - | 158600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | 159600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * V | 160600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * NOTE: +-----------+ | 161600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | 162600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * transitions | Terminated|<------------------+ 163600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * labeled with | | 164600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * the event +-----------+ 165600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * over the action 166600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * to take 167600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 168600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Figure 6: non-INVITE client transaction 169600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 170600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 171600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 172600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 173600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 174600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 175600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * </pre> 176600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 177600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 178600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @author M. Ranganathan 179600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 180600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @version 1.2 $Revision: 1.122 $ $Date: 2009/12/17 23:33:52 $ 181600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 182600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wangpublic class SIPClientTransaction extends SIPTransaction implements ServerResponseInterface, 183600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang javax.sip.ClientTransaction, gov.nist.javax.sip.ClientTransactionExt { 184600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 185600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // a SIP Client transaction may belong simultaneously to multiple 186600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // dialogs in the early state. These dialogs all have 187600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // the same call ID and same From tag but different to tags. 188600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 189600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang private ConcurrentHashMap<String,SIPDialog> sipDialogs; 190600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 191600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang private SIPRequest lastRequest; 192600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 193600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang private int viaPort; 194600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 195600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang private String viaHost; 196600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 197600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Real ResponseInterface to pass messages to 198600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang private transient ServerResponseInterface respondTo; 199600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 200600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang private SIPDialog defaultDialog; 201600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 202600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang private Hop nextHop; 203600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 204600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang private boolean notifyOnRetransmit; 205600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 206600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang private boolean timeoutIfStillInCallingState; 207600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 208600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang private int callingStateTimeoutCount; 209600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 210600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public class TransactionTimer extends SIPStackTimerTask { 211600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 212600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public TransactionTimer() { 213600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 214600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 215600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 216600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang protected void runTask() { 217600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPClientTransaction clientTransaction; 218600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPTransactionStack sipStack; 219600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang clientTransaction = SIPClientTransaction.this; 220600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack = clientTransaction.sipStack; 221600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 222600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // If the transaction has terminated, 223600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (clientTransaction.isTerminated()) { 224600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 225600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) { 226600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug( 227600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang "removing = " + clientTransaction + " isReliable " 228600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang + clientTransaction.isReliable()); 229600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 230600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 231600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.removeTransaction(clientTransaction); 232600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 233600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang try { 234600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.cancel(); 235600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 236600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } catch (IllegalStateException ex) { 237600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (!sipStack.isAlive()) 238600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return; 239600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 240600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 241600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Client transaction terminated. Kill connection if 242600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // this is a TCP after the linger timer has expired. 243600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // The linger timer is needed to allow any pending requests to 244600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // return responses. 245600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if ((!sipStack.cacheClientConnections) && clientTransaction.isReliable()) { 246600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 247600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang int newUseCount = --clientTransaction.getMessageChannel().useCount; 248600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (newUseCount <= 0) { 249600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Let the connection linger for a while and then close 250600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // it. 251600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang TimerTask myTimer = new LingerTimer(); 252600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getTimer().schedule(myTimer, 253600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPTransactionStack.CONNECTION_LINGER_TIME * 1000); 254600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 255600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 256600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 257600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Cache the client connections so dont close the 258600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // connection. This keeps the connection open permanently 259600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // until the client disconnects. 260600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled() && clientTransaction.isReliable()) { 261600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang int useCount = clientTransaction.getMessageChannel().useCount; 262600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) 263600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug("Client Use Count = " + useCount); 264600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 265600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 266600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 267600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 268600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // If this transaction has not 269600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // terminated, 270600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Fire the transaction timer. 271600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang clientTransaction.fireTimer(); 272600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 273600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 274600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 275600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 276600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 277600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 278600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 279600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 280600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Creates a new client transaction. 281600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 282600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param newSIPStack Transaction stack this transaction belongs to. 283600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param newChannelToUse Channel to encapsulate. 284600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @return the created client transaction. 285600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 286600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang protected SIPClientTransaction(SIPTransactionStack newSIPStack, MessageChannel newChannelToUse) { 287600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang super(newSIPStack, newChannelToUse); 288600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Create a random branch parameter for this transaction 289600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // setBranch( SIPConstants.BRANCH_MAGIC_COOKIE + 290600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Integer.toHexString( hashCode( ) ) ); 291600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang setBranch(Utils.getInstance().generateBranchId()); 292600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.messageProcessor = newChannelToUse.messageProcessor; 293600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setEncapsulatedChannel(newChannelToUse); 294600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.notifyOnRetransmit = false; 295600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.timeoutIfStillInCallingState = false; 296600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 297600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // This semaphore guards the listener from being 298600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // re-entered for this transaction. That is 299600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // for a give tx, the listener is called at most 300600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // once with an outstanding request. 301600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 302600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) { 303600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug("Creating clientTransaction " + this); 304600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logStackTrace(); 305600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 306600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // this.startTransactionTimer(); 307600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.sipDialogs = new ConcurrentHashMap(); 308600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 309600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 310600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 311600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Sets the real ResponseInterface this transaction encapsulates. 312600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 313600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param newRespondTo ResponseInterface to send messages to. 314600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 315600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public void setResponseInterface(ServerResponseInterface newRespondTo) { 316600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) { 317600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug( 318600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang "Setting response interface for " + this + " to " + newRespondTo); 319600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (newRespondTo == null) { 320600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logStackTrace(); 321600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug("WARNING -- setting to null!"); 322600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 323600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 324600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 325600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang respondTo = newRespondTo; 326600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 327600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 328600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 329600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 330600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Returns this transaction. 331600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 332600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public MessageChannel getRequestChannel() { 333600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 334600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return this; 335600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 336600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 337600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 338600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 339600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Deterines if the message is a part of this transaction. 340600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 341600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param messageToTest Message to check if it is part of this transaction. 342600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 343600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @return true if the message is part of this transaction, false if not. 344600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 345600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public boolean isMessagePartOfTransaction(SIPMessage messageToTest) { 346600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 347600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // List of Via headers in the message to test 348600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang ViaList viaHeaders = messageToTest.getViaHeaders(); 349600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Flags whether the select message is part of this transaction 350600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang boolean transactionMatches; 351600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang String messageBranch = ((Via) viaHeaders.getFirst()).getBranch(); 352600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang boolean rfc3261Compliant = getBranch() != null 353600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && messageBranch != null 354600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && getBranch().toLowerCase().startsWith( 355600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPConstants.BRANCH_MAGIC_COOKIE_LOWER_CASE) 356600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && messageBranch.toLowerCase().startsWith( 357600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPConstants.BRANCH_MAGIC_COOKIE_LOWER_CASE); 358600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 359600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang transactionMatches = false; 360600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (TransactionState.COMPLETED == this.getState()) { 361600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (rfc3261Compliant) { 362600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang transactionMatches = getBranch().equalsIgnoreCase( 363600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang ((Via) viaHeaders.getFirst()).getBranch()) 364600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && getMethod().equals(messageToTest.getCSeq().getMethod()); 365600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 366600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang transactionMatches = getBranch().equals(messageToTest.getTransactionId()); 367600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 368600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (!isTerminated()) { 369600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (rfc3261Compliant) { 370600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (viaHeaders != null) { 371600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // If the branch parameter is the 372600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // same as this transaction and the method is the same, 373600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (getBranch().equalsIgnoreCase(((Via) viaHeaders.getFirst()).getBranch())) { 374600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang transactionMatches = getOriginalRequest().getCSeq().getMethod().equals( 375600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang messageToTest.getCSeq().getMethod()); 376600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 377600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 378600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 379600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 380600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // not RFC 3261 compliant. 381600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (getBranch() != null) { 382600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang transactionMatches = getBranch().equalsIgnoreCase( 383600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang messageToTest.getTransactionId()); 384600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 385600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang transactionMatches = getOriginalRequest().getTransactionId() 386600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang .equalsIgnoreCase(messageToTest.getTransactionId()); 387600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 388600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 389600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 390600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 391600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 392600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return transactionMatches; 393600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 394600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 395600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 396600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 397600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Send a request message through this transaction and onto the client. 398600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 399600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param messageToSend Request to process and send. 400600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 401600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public void sendMessage(SIPMessage messageToSend) throws IOException { 402600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 403600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang try { 404600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Message typecast as a request 405600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPRequest transactionRequest; 406600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 407600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang transactionRequest = (SIPRequest) messageToSend; 408600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 409600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Set the branch id for the top via header. 410600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang Via topVia = (Via) transactionRequest.getViaHeaders().getFirst(); 411600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Tack on a branch identifier to match responses. 412600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang try { 413600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang topVia.setBranch(getBranch()); 414600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } catch (java.text.ParseException ex) { 415600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 416600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 417600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) { 418600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug("Sending Message " + messageToSend); 419600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug("TransactionState " + this.getState()); 420600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 421600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // If this is the first request for this transaction, 422600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (TransactionState.PROCEEDING == getState() 423600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang || TransactionState.CALLING == getState()) { 424600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 425600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // If this is a TU-generated ACK request, 426600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (transactionRequest.getMethod().equals(Request.ACK)) { 427600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 428600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Send directly to the underlying 429600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // transport and close this transaction 430600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (isReliable()) { 431600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TERMINATED); 432600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 433600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.COMPLETED); 434600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 435600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // BUGBUG -- This suppresses sending the ACK uncomment this 436600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // to 437600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // test 4xx retransmission 438600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // if (transactionRequest.getMethod() != Request.ACK) 439600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang super.sendMessage(transactionRequest); 440600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return; 441600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 442600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 443600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 444600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 445600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang try { 446600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 447600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Send the message to the server 448600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang lastRequest = transactionRequest; 449600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (getState() == null) { 450600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Save this request as the one this transaction 451600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // is handling 452600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang setOriginalRequest(transactionRequest); 453600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Change to trying/calling state 454600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Set state first to avoid race condition.. 455600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 456600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (transactionRequest.getMethod().equals(Request.INVITE)) { 457600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.CALLING); 458600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (transactionRequest.getMethod().equals(Request.ACK)) { 459600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Acks are never retransmitted. 460600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TERMINATED); 461600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 462600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TRYING); 463600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 464600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (!isReliable()) { 465600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang enableRetransmissionTimer(); 466600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 467600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (isInviteTransaction()) { 468600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang enableTimeoutTimer(TIMER_B); 469600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 470600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang enableTimeoutTimer(TIMER_F); 471600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 472600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 473600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // BUGBUG This supresses sending ACKS -- uncomment to test 474600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // 4xx retransmission. 475600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // if (transactionRequest.getMethod() != Request.ACK) 476600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang super.sendMessage(transactionRequest); 477600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 478600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } catch (IOException e) { 479600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 480600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TERMINATED); 481600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw e; 482600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 483600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 484600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } finally { 485600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.isMapped = true; 486600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.startTransactionTimer(); 487600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 488600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 489600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 490600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 491600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 492600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 493600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Process a new response message through this transaction. If necessary, this message will 494600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * also be passed onto the TU. 495600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 496600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param transactionResponse Response to process. 497600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param sourceChannel Channel that received this message. 498600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 499600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public synchronized void processResponse(SIPResponse transactionResponse, 500600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang MessageChannel sourceChannel, SIPDialog dialog) { 501600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 502600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // If the state has not yet been assigned then this is a 503600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // spurious response. 504600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 505600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (getState() == null) 506600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return; 507600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 508600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Ignore 1xx 509600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if ((TransactionState.COMPLETED == this.getState() || TransactionState.TERMINATED == this 510600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang .getState()) 511600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && transactionResponse.getStatusCode() / 100 == 1) { 512600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return; 513600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 514600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 515600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) { 516600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug( 517600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang "processing " + transactionResponse.getFirstLine() + "current state = " 518600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang + getState()); 519600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug("dialog = " + dialog); 520600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 521600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 522600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.lastResponse = transactionResponse; 523600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 524600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 525600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * JvB: this is now duplicate with code in the other processResponse 526600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 527600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * if (dialog != null && transactionResponse.getStatusCode() != 100 && 528600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * (transactionResponse.getTo().getTag() != null || sipStack .isRfc2543Supported())) { // 529600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * add the route before you process the response. dialog.setLastResponse(this, 530600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * transactionResponse); this.setDialog(dialog, transactionResponse.getDialogId(false)); } 531600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 532600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 533600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang try { 534600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (isInviteTransaction()) 535600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang inviteClientTransaction(transactionResponse, sourceChannel, dialog); 536600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang else 537600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang nonInviteClientTransaction(transactionResponse, sourceChannel, dialog); 538600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } catch (IOException ex) { 539600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) 540600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logException(ex); 541600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TERMINATED); 542600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang raiseErrorEvent(SIPTransactionErrorEvent.TRANSPORT_ERROR); 543600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 544600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 545600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 546600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 547600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Implements the state machine for invite client transactions. 548600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 549600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * <pre> 550600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 551600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 552600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 553600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 554600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 555600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * |Request from TU 556600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * |send request 557600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Timer E V 558600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * send request +-----------+ 559600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +---------| |-------------------+ 560600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | Trying | Timer F | 561600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +-------->| | or Transport Err.| 562600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +-----------+ inform TU | 563600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 200-699 | | | 564600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * resp. to TU | |1xx | 565600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +---------------+ |resp. to TU | 566600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | 567600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | Timer E V Timer F | 568600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | send req +-----------+ or Transport Err. | 569600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +---------| | inform TU | 570600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | |Proceeding |------------------>| 571600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-------->| |-----+ | 572600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-----------+ |1xx | 573600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | ˆ |resp to TU | 574600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | 200-699 | +--------+ | 575600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | resp. to TU | | 576600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | 577600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | V | 578600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-----------+ | 579600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | | 580600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | Completed | | 581600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | | 582600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-----------+ | 583600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | ˆ | | 584600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | Timer K | 585600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +--------------+ | - | 586600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | 587600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * V | 588600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * NOTE: +-----------+ | 589600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | 590600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * transitions | Terminated|<------------------+ 591600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * labeled with | | 592600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * the event +-----------+ 593600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * over the action 594600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * to take 595600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 596600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Figure 6: non-INVITE client transaction 597600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 598600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 599600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 600600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 601600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * </pre> 602600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 603600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param transactionResponse -- transaction response received. 604600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param sourceChannel - source channel on which the response was received. 605600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 606600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang private void nonInviteClientTransaction(SIPResponse transactionResponse, 607600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang MessageChannel sourceChannel, SIPDialog sipDialog) throws IOException { 608600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang int statusCode = transactionResponse.getStatusCode(); 609600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (TransactionState.TRYING == this.getState()) { 610600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (statusCode / 100 == 1) { 611600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.PROCEEDING); 612600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang enableRetransmissionTimer(MAXIMUM_RETRANSMISSION_TICK_COUNT); 613600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang enableTimeoutTimer(TIMER_F); 614600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // According to RFC, the TU has to be informed on 615600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // this transition. 616600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (respondTo != null) { 617600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang respondTo.processResponse(transactionResponse, this, sipDialog); 618600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 619600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.semRelease(); 620600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 621600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (200 <= statusCode && statusCode <= 699) { 622600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Send the response up to the TU. 623600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (respondTo != null) { 624600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang respondTo.processResponse(transactionResponse, this, sipDialog); 625600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 626600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.semRelease(); 627600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 628600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (!isReliable()) { 629600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.COMPLETED); 630600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang enableTimeoutTimer(TIMER_K); 631600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 632600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TERMINATED); 633600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 634600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 635600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (TransactionState.PROCEEDING == this.getState()) { 636600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (statusCode / 100 == 1) { 637600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (respondTo != null) { 638600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang respondTo.processResponse(transactionResponse, this, sipDialog); 639600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 640600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.semRelease(); 641600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 642600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (200 <= statusCode && statusCode <= 699) { 643600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (respondTo != null) { 644600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang respondTo.processResponse(transactionResponse, this, sipDialog); 645600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 646600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.semRelease(); 647600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 648600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang disableRetransmissionTimer(); 649600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang disableTimeoutTimer(); 650600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (!isReliable()) { 651600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.COMPLETED); 652600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang enableTimeoutTimer(TIMER_K); 653600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 654600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TERMINATED); 655600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 656600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 657600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 658600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) { 659600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug( 660600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang " Not sending response to TU! " + getState()); 661600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 662600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.semRelease(); 663600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 664600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 665600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 666600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 667600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Implements the state machine for invite client transactions. 668600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 669600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * <pre> 670600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 671600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 672600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 673600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 674600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 675600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * |INVITE from TU 676600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Timer A fires |INVITE sent 677600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Reset A, V Timer B fires 678600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * INVITE sent +-----------+ or Transport Err. 679600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +---------| |---------------+inform TU 680600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | Calling | | 681600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +-------->| |-------------->| 682600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +-----------+ 2xx | 683600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | 2xx to TU | 684600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | |1xx | 685600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 300-699 +---------------+ |1xx to TU | 686600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * ACK sent | | | 687600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * resp. to TU | 1xx V | 688600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | 1xx to TU -----------+ | 689600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +---------| | | 690600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | |Proceeding |-------------->| 691600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-------->| | 2xx | 692600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-----------+ 2xx to TU | 693600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | 300-699 | | 694600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | ACK sent, | | 695600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | resp. to TU| | 696600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | NOTE: 697600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | 300-699 V | 698600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | ACK sent +-----------+Transport Err. | transitions 699600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +---------| |Inform TU | labeled with 700600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | Completed |-------------->| the event 701600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-------->| | | over the action 702600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | +-----------+ | to take 703600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | ˆ | | 704600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | Timer D fires | 705600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +--------------+ | - | 706600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | 707600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * V | 708600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +-----------+ | 709600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | | 710600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | Terminated|<--------------+ 711600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * | | 712600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * +-----------+ 713600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 714600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 715600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 716600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 717600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * </pre> 718600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 719600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param transactionResponse -- transaction response received. 720600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param sourceChannel - source channel on which the response was received. 721600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 722600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 723600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang private void inviteClientTransaction(SIPResponse transactionResponse, 724600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang MessageChannel sourceChannel, SIPDialog dialog) throws IOException { 725600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang int statusCode = transactionResponse.getStatusCode(); 726600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 727600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (TransactionState.TERMINATED == this.getState()) { 728600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang boolean ackAlreadySent = false; 729600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (dialog != null && dialog.isAckSeen() && dialog.getLastAckSent() != null) { 730600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (dialog.getLastAckSent().getCSeq().getSeqNumber() == transactionResponse.getCSeq() 731600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang .getSeqNumber() 732600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && transactionResponse.getFromTag().equals( 733600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang dialog.getLastAckSent().getFromTag())) { 734600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // the last ack sent corresponded to this response 735600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang ackAlreadySent = true; 736600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 737600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 738600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // retransmit the ACK for this response. 739600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (dialog!= null && ackAlreadySent 740600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && transactionResponse.getCSeq().getMethod().equals(dialog.getMethod())) { 741600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang try { 742600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Found the dialog - resend the ACK and 743600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // dont pass up the null transaction 744600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) 745600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug("resending ACK"); 746600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 747600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang dialog.resendAck(); 748600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } catch (SipException ex) { 749600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // What to do here ?? kill the dialog? 750600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 751600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 752600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 753600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.semRelease(); 754600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return; 755600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (TransactionState.CALLING == this.getState()) { 756600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (statusCode / 100 == 2) { 757600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 758600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // JvB: do this ~before~ calling the application, to avoid 759600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // retransmissions 760600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // of the INVITE after app sends ACK 761600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang disableRetransmissionTimer(); 762600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang disableTimeoutTimer(); 763600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TERMINATED); 764600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 765600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // 200 responses are always seen by TU. 766600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (respondTo != null) 767600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang respondTo.processResponse(transactionResponse, this, dialog); 768600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang else { 769600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.semRelease(); 770600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 771600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 772600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (statusCode / 100 == 1) { 773600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang disableRetransmissionTimer(); 774600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang disableTimeoutTimer(); 775600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.PROCEEDING); 776600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 777600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (respondTo != null) 778600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang respondTo.processResponse(transactionResponse, this, dialog); 779600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang else { 780600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.semRelease(); 781600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 782600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 783600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (300 <= statusCode && statusCode <= 699) { 784600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Send back an ACK request 785600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 786600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang try { 787600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sendMessage((SIPRequest) createErrorAck()); 788600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 789600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } catch (Exception ex) { 790600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logError( 791600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang "Unexpected Exception sending ACK -- sending error AcK ", ex); 792600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 793600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 794600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 795600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 796600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * When in either the "Calling" or "Proceeding" states, reception of response with 797600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * status code from 300-699 MUST cause the client transaction to transition to 798600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * "Completed". The client transaction MUST pass the received response up to the 799600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * TU, and the client transaction MUST generate an ACK request. 800600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 801600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 802600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (respondTo != null) { 803600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang respondTo.processResponse(transactionResponse, this, dialog); 804600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 805600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.semRelease(); 806600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 807600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 808600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.getDialog() != null && ((SIPDialog)this.getDialog()).isBackToBackUserAgent()) { 809600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang ((SIPDialog) this.getDialog()).releaseAckSem(); 810600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 811600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 812600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (!isReliable()) { 813600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.COMPLETED); 814600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang enableTimeoutTimer(TIMER_D); 815600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 816600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Proceed immediately to the TERMINATED state. 817600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TERMINATED); 818600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 819600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 820600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (TransactionState.PROCEEDING == this.getState()) { 821600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (statusCode / 100 == 1) { 822600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (respondTo != null) { 823600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang respondTo.processResponse(transactionResponse, this, dialog); 824600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 825600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.semRelease(); 826600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 827600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (statusCode / 100 == 2) { 828600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TERMINATED); 829600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (respondTo != null) { 830600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang respondTo.processResponse(transactionResponse, this, dialog); 831600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 832600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.semRelease(); 833600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 834600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 835600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (300 <= statusCode && statusCode <= 699) { 836600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Send back an ACK request 837600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang try { 838600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sendMessage((SIPRequest) createErrorAck()); 839600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } catch (Exception ex) { 840600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang InternalErrorHandler.handleException(ex); 841600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 842600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 843600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.getDialog() != null) { 844600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang ((SIPDialog) this.getDialog()).releaseAckSem(); 845600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 846600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // JvB: update state before passing to app 847600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (!isReliable()) { 848600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.COMPLETED); 849600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.enableTimeoutTimer(TIMER_D); 850600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 851600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TERMINATED); 852600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 853600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 854600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Pass up to the TU for processing. 855600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (respondTo != null) 856600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang respondTo.processResponse(transactionResponse, this, dialog); 857600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang else { 858600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.semRelease(); 859600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 860600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 861600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // JvB: duplicate with line 874 862600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // if (!isReliable()) { 863600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // enableTimeoutTimer(TIMER_D); 864600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // } 865600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 866600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (TransactionState.COMPLETED == this.getState()) { 867600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (300 <= statusCode && statusCode <= 699) { 868600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Send back an ACK request 869600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang try { 870600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sendMessage((SIPRequest) createErrorAck()); 871600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } catch (Exception ex) { 872600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang InternalErrorHandler.handleException(ex); 873600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } finally { 874600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.semRelease(); 875600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 876600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 877600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 878600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 879600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 880600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 881600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 882600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 883600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * (non-Javadoc) 884600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 885600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @see javax.sip.ClientTransaction#sendRequest() 886600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 887600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public void sendRequest() throws SipException { 888600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPRequest sipRequest = this.getOriginalRequest(); 889600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 890600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.getState() != null) 891600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("Request already sent"); 892600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 893600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) { 894600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug("sendRequest() " + sipRequest); 895600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 896600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 897600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang try { 898600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipRequest.checkHeaders(); 899600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } catch (ParseException ex) { 900600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) 901600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logError("missing required header"); 902600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException(ex.getMessage()); 903600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 904600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 905600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (getMethod().equals(Request.SUBSCRIBE) 906600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && sipRequest.getHeader(ExpiresHeader.NAME) == null) { 907600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 908600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * If no "Expires" header is present in a SUBSCRIBE request, the implied default is 909600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * defined by the event package being used. 910600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 911600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 912600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) 913600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logWarning( 914600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang "Expires header missing in outgoing subscribe --" 915600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang + " Notifier will assume implied value on event package"); 916600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 917600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang try { 918600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 919600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * This check is removed because it causes problems for load balancers ( See issue 920600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 136) reported by Raghav Ramesh ( BT ) 921600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 922600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 923600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.getOriginalRequest().getMethod().equals(Request.CANCEL) 924600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && sipStack.isCancelClientTransactionChecked()) { 925600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPClientTransaction ct = (SIPClientTransaction) sipStack.findCancelTransaction( 926600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.getOriginalRequest(), false); 927600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (ct == null) { 928600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 929600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * If the original request has generated a final response, the CANCEL SHOULD 930600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * NOT be sent, as it is an effective no-op, since CANCEL has no effect on 931600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * requests that have already generated a final response. 932600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 933600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("Could not find original tx to cancel. RFC 3261 9.1"); 934600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (ct.getState() == null) { 935600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException( 936600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang "State is null no provisional response yet -- cannot cancel RFC 3261 9.1"); 937600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (!ct.getMethod().equals(Request.INVITE)) { 938600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("Cannot cancel non-invite requests RFC 3261 9.1"); 939600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 940600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else 941600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 942600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.getOriginalRequest().getMethod().equals(Request.BYE) 943600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang || this.getOriginalRequest().getMethod().equals(Request.NOTIFY)) { 944600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPDialog dialog = sipStack.getDialog(this.getOriginalRequest() 945600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang .getDialogId(false)); 946600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // I want to behave like a user agent so send the BYE using the 947600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Dialog 948600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.getSipProvider().isAutomaticDialogSupportEnabled() && dialog != null) { 949600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException( 950600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang "Dialog is present and AutomaticDialogSupport is enabled for " 951600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang + " the provider -- Send the Request using the Dialog.sendRequest(transaction)"); 952600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 953600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 954600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Only map this after the fist request is sent out. 955600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.getMethod().equals(Request.INVITE)) { 956600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPDialog dialog = this.getDefaultDialog(); 957600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 958600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (dialog != null && dialog.isBackToBackUserAgent()) { 959600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Block sending re-INVITE till we see the ACK. 960600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if ( ! dialog.takeAckSem() ) { 961600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException ("Failed to take ACK semaphore"); 962600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 963600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 964600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 965600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 966600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.isMapped = true; 967600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.sendMessage(sipRequest); 968600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 969600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } catch (IOException ex) { 970600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TERMINATED); 971600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("IO Error sending request", ex); 972600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 973600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 974600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 975600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 976600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 977600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 978600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Called by the transaction stack when a retransmission timer fires. 979600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 980600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang protected void fireRetransmissionTimer() { 981600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 982600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang try { 983600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 984600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Resend the last request sent 985600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.getState() == null || !this.isMapped) 986600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return; 987600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 988600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang boolean inv = isInviteTransaction(); 989600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang TransactionState s = this.getState(); 990600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 991600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // JvB: INVITE CTs only retransmit in CALLING, non-INVITE in both TRYING and 992600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // PROCEEDING 993600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Bug-fix for non-INVITE transactions not retransmitted when 1xx response received 994600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if ((inv && TransactionState.CALLING == s) 995600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang || (!inv && (TransactionState.TRYING == s || TransactionState.PROCEEDING == s))) { 996600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // If the retransmission filter is disabled then 997600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // retransmission of the INVITE is the application 998600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // responsibility. 999600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1000600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (lastRequest != null) { 1001600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.generateTimeStampHeader 1002600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && lastRequest.getHeader(TimeStampHeader.NAME) != null) { 1003600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang long milisec = System.currentTimeMillis(); 1004600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang TimeStamp timeStamp = new TimeStamp(); 1005600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang try { 1006600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang timeStamp.setTimeStamp(milisec); 1007600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } catch (InvalidArgumentException ex) { 1008600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang InternalErrorHandler.handleException(ex); 1009600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1010600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang lastRequest.setHeader(timeStamp); 1011600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1012600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang super.sendMessage(lastRequest); 1013600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.notifyOnRetransmit) { 1014600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang TimeoutEvent txTimeout = new TimeoutEvent(this.getSipProvider(), this, 1015600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang Timeout.RETRANSMIT); 1016600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.getSipProvider().handleEvent(txTimeout, this); 1017600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1018600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.timeoutIfStillInCallingState 1019600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && this.getState() == TransactionState.CALLING) { 1020600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.callingStateTimeoutCount--; 1021600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (callingStateTimeoutCount == 0) { 1022600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang TimeoutEvent timeoutEvent = new TimeoutEvent(this.getSipProvider(), 1023600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this, Timeout.RETRANSMIT); 1024600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.getSipProvider().handleEvent(timeoutEvent, this); 1025600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.timeoutIfStillInCallingState = false; 1026600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1027600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1028600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1029600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1030600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1031600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1032600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } catch (IOException e) { 1033600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.raiseIOExceptionEvent(); 1034600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang raiseErrorEvent(SIPTransactionErrorEvent.TRANSPORT_ERROR); 1035600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1036600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1037600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1038600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1039600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1040600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Called by the transaction stack when a timeout timer fires. 1041600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1042600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang protected void fireTimeoutTimer() { 1043600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1044600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) 1045600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug("fireTimeoutTimer " + this); 1046600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1047600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPDialog dialog = (SIPDialog) this.getDialog(); 1048600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (TransactionState.CALLING == this.getState() 1049600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang || TransactionState.TRYING == this.getState() 1050600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang || TransactionState.PROCEEDING == this.getState()) { 1051600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Timeout occured. If this is asociated with a transaction 1052600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // creation then kill the dialog. 1053600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (dialog != null 1054600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && (dialog.getState() == null || dialog.getState() == DialogState.EARLY)) { 1055600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (((SIPTransactionStack) getSIPStack()).isDialogCreated(this 1056600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang .getOriginalRequest().getMethod())) { 1057600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // If this is a re-invite we do not delete the dialog even 1058600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // if the 1059600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // reinvite times out. Else 1060600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // terminate the enclosing dialog. 1061600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang dialog.delete(); 1062600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1063600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (dialog != null) { 1064600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Guard against the case of BYE time out. 1065600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1066600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (getOriginalRequest().getMethod().equalsIgnoreCase(Request.BYE) 1067600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && dialog.isTerminatedOnBye()) { 1068600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Terminate the associated dialog on BYE Timeout. 1069600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang dialog.delete(); 1070600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1071600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1072600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1073600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (TransactionState.COMPLETED != this.getState()) { 1074600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang raiseErrorEvent(SIPTransactionErrorEvent.TIMEOUT_ERROR); 1075600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Got a timeout error on a cancel. 1076600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.getOriginalRequest().getMethod().equalsIgnoreCase(Request.CANCEL)) { 1077600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPClientTransaction inviteTx = (SIPClientTransaction) this.getOriginalRequest() 1078600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang .getInviteTransaction(); 1079600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (inviteTx != null 1080600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && ((inviteTx.getState() == TransactionState.CALLING || inviteTx 1081600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang .getState() == TransactionState.PROCEEDING)) 1082600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && inviteTx.getDialog() != null) { 1083600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 1084600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * A proxy server should have started TIMER C and take care of the Termination 1085600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * using transaction.terminate() by itself (i.e. this is not the job of the 1086600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * stack at this point but we do it to be nice. 1087600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1088600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang inviteTx.setState(TransactionState.TERMINATED); 1089600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1090600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1091600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1092600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1093600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 1094600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TERMINATED); 1095600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1096600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1097600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1098600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1099600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 1100600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * (non-Javadoc) 1101600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 1102600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @see javax.sip.ClientTransaction#createCancel() 1103600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1104600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public Request createCancel() throws SipException { 1105600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPRequest originalRequest = this.getOriginalRequest(); 1106600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (originalRequest == null) 1107600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("Bad state " + getState()); 1108600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (!originalRequest.getMethod().equals(Request.INVITE)) 1109600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("Only INIVTE may be cancelled"); 1110600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1111600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (originalRequest.getMethod().equalsIgnoreCase(Request.ACK)) 1112600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("Cannot Cancel ACK!"); 1113600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang else { 1114600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPRequest cancelRequest = originalRequest.createCancelRequest(); 1115600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang cancelRequest.setInviteTransaction(this); 1116600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return cancelRequest; 1117600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1118600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1119600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1120600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 1121600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * (non-Javadoc) 1122600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 1123600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @see javax.sip.ClientTransaction#createAck() 1124600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1125600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public Request createAck() throws SipException { 1126600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPRequest originalRequest = this.getOriginalRequest(); 1127600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (originalRequest == null) 1128600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("bad state " + getState()); 1129600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (getMethod().equalsIgnoreCase(Request.ACK)) { 1130600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("Cannot ACK an ACK!"); 1131600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (lastResponse == null) { 1132600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("bad Transaction state"); 1133600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (lastResponse.getStatusCode() < 200) { 1134600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) { 1135600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug("lastResponse = " + lastResponse); 1136600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1137600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("Cannot ACK a provisional response!"); 1138600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1139600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPRequest ackRequest = originalRequest.createAckRequest((To) lastResponse.getTo()); 1140600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Pull the record route headers from the last reesponse. 1141600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang RecordRouteList recordRouteList = lastResponse.getRecordRouteHeaders(); 1142600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (recordRouteList == null) { 1143600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // If the record route list is null then we can 1144600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // construct the ACK from the specified contact header. 1145600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Note the 3xx check here because 3xx is a redirect. 1146600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // The contact header for the 3xx is the redirected 1147600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // location so we cannot use that to construct the 1148600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // request URI. 1149600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (lastResponse.getContactHeaders() != null 1150600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && lastResponse.getStatusCode() / 100 != 3) { 1151600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang Contact contact = (Contact) lastResponse.getContactHeaders().getFirst(); 1152600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang javax.sip.address.URI uri = (javax.sip.address.URI) contact.getAddress().getURI() 1153600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang .clone(); 1154600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang ackRequest.setRequestURI(uri); 1155600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1156600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return ackRequest; 1157600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1158600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1159600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang ackRequest.removeHeader(RouteHeader.NAME); 1160600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang RouteList routeList = new RouteList(); 1161600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // start at the end of the list and walk backwards 1162600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang ListIterator<RecordRoute> li = recordRouteList.listIterator(recordRouteList.size()); 1163600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang while (li.hasPrevious()) { 1164600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang RecordRoute rr = (RecordRoute) li.previous(); 1165600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1166600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang Route route = new Route(); 1167600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang route.setAddress((AddressImpl) ((AddressImpl) rr.getAddress()).clone()); 1168600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang route.setParameters((NameValueList) rr.getParameters().clone()); 1169600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang routeList.add(route); 1170600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1171600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1172600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang Contact contact = null; 1173600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (lastResponse.getContactHeaders() != null) { 1174600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang contact = (Contact) lastResponse.getContactHeaders().getFirst(); 1175600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1176600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1177600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (!((SipURI) ((Route) routeList.getFirst()).getAddress().getURI()).hasLrParam()) { 1178600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1179600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Contact may not yet be there (bug reported by Andreas B). 1180600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1181600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang Route route = null; 1182600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (contact != null) { 1183600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang route = new Route(); 1184600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang route.setAddress((AddressImpl) ((AddressImpl) (contact.getAddress())).clone()); 1185600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1186600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1187600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang Route firstRoute = (Route) routeList.getFirst(); 1188600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang routeList.removeFirst(); 1189600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang javax.sip.address.URI uri = firstRoute.getAddress().getURI(); 1190600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang ackRequest.setRequestURI(uri); 1191600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1192600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (route != null) 1193600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang routeList.add(route); 1194600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1195600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang ackRequest.addHeader(routeList); 1196600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 1197600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (contact != null) { 1198600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang javax.sip.address.URI uri = (javax.sip.address.URI) contact.getAddress().getURI() 1199600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang .clone(); 1200600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang ackRequest.setRequestURI(uri); 1201600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang ackRequest.addHeader(routeList); 1202600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1203600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1204600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return ackRequest; 1205600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1206600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1207600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1208600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 1209600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Creates an ACK for an error response, according to RFC3261 section 17.1.1.3 1210600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 1211600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Note that this is different from an ACK for 2xx 1212600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1213600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang private final Request createErrorAck() throws SipException, ParseException { 1214600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPRequest originalRequest = this.getOriginalRequest(); 1215600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (originalRequest == null) 1216600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("bad state " + getState()); 1217600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (!getMethod().equals(Request.INVITE)) { 1218600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("Can only ACK an INVITE!"); 1219600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (lastResponse == null) { 1220600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("bad Transaction state"); 1221600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else if (lastResponse.getStatusCode() < 200) { 1222600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) { 1223600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug("lastResponse = " + lastResponse); 1224600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1225600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new SipException("Cannot ACK a provisional response!"); 1226600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1227600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return originalRequest.createErrorAck((To) lastResponse.getTo()); 1228600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1229600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1230600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1231600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Set the port of the recipient. 1232600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1233600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang protected void setViaPort(int port) { 1234600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.viaPort = port; 1235600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1236600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1237600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1238600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Set the port of the recipient. 1239600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1240600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang protected void setViaHost(String host) { 1241600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.viaHost = host; 1242600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1243600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1244600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1245600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Get the port of the recipient. 1246600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1247600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public int getViaPort() { 1248600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return this.viaPort; 1249600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1250600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1251600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1252600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Get the host of the recipient. 1253600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1254600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public String getViaHost() { 1255600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return this.viaHost; 1256600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1257600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1258600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1259600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * get the via header for an outgoing request. 1260600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1261600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public Via getOutgoingViaHeader() { 1262600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return this.getMessageProcessor().getViaHeader(); 1263600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1264600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1265600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1266600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * This is called by the stack after a non-invite client transaction goes to completed state. 1267600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1268600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public void clearState() { 1269600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // reduce the state to minimum 1270600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // This assumes that the application will not need 1271600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // to access the request once the transaction is 1272600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // completed. 1273600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // TODO -- revisit this - results in a null pointer 1274600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // occuring occasionally. 1275600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // this.lastRequest = null; 1276600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // this.originalRequest = null; 1277600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // this.lastResponse = null; 1278600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1279600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1280600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1281600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Sets a timeout after which the connection is closed (provided the server does not use the 1282600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * connection for outgoing requests in this time period) and calls the superclass to set 1283600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * state. 1284600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1285600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public void setState(TransactionState newState) { 1286600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Set this timer for connection caching 1287600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // of incoming connections. 1288600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (newState == TransactionState.TERMINATED && this.isReliable() 1289600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && (!getSIPStack().cacheClientConnections)) { 1290600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Set a time after which the connection 1291600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // is closed. 1292600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.collectionTime = TIMER_J; 1293600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1294600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1295600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (super.getState() != TransactionState.COMPLETED 1296600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && (newState == TransactionState.COMPLETED || newState == TransactionState.TERMINATED)) { 1297600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.decrementActiveClientTransactionCount(); 1298600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1299600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang super.setState(newState); 1300600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1301600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1302600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1303600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Start the timer task. 1304600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1305600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang protected void startTransactionTimer() { 1306600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.transactionTimerStarted.compareAndSet(false, true)) { 1307600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang TimerTask myTimer = new TransactionTimer(); 1308600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if ( sipStack.getTimer() != null ) { 1309600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getTimer().schedule(myTimer, BASE_TIMER_INTERVAL, BASE_TIMER_INTERVAL); 1310600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1311600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1312600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1313600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1314600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 1315600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Terminate a transaction. This marks the tx as terminated The tx scanner will run and remove 1316600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * the tx. (non-Javadoc) 1317600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 1318600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @see javax.sip.Transaction#terminate() 1319600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1320600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public void terminate() throws ObjectInUseException { 1321600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setState(TransactionState.TERMINATED); 1322600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1323600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1324600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1325600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1326600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Check if the From tag of the response matches the from tag of the original message. A 1327600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Response with a tag mismatch should be dropped if a Dialog has been created for the 1328600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * original request. 1329600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 1330600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param sipResponse the response to check. 1331600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @return true if the check passes. 1332600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1333600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public boolean checkFromTag(SIPResponse sipResponse) { 1334600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang String originalFromTag = ((SIPRequest) this.getRequest()).getFromTag(); 1335600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.defaultDialog != null) { 1336600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (originalFromTag == null ^ sipResponse.getFrom().getTag() == null) { 1337600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) 1338600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug("From tag mismatch -- dropping response"); 1339600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return false; 1340600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1341600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (originalFromTag != null 1342600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && !originalFromTag.equalsIgnoreCase(sipResponse.getFrom().getTag())) { 1343600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) 1344600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug("From tag mismatch -- dropping response"); 1345600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return false; 1346600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1347600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1348600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return true; 1349600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1350600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1351600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1352600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 1353600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * (non-Javadoc) 1354600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 1355600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @see gov.nist.javax.sip.stack.ServerResponseInterface#processResponse(gov.nist.javax.sip.message.SIPResponse, 1356600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * gov.nist.javax.sip.stack.MessageChannel) 1357600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1358600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public void processResponse(SIPResponse sipResponse, MessageChannel incomingChannel) { 1359600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1360600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // If a dialog has already been created for this response, 1361600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // pass it up. 1362600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPDialog dialog = null; 1363600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang String method = sipResponse.getCSeq().getMethod(); 1364600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang String dialogId = sipResponse.getDialogId(false); 1365600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (method.equals(Request.CANCEL) && lastRequest != null) { 1366600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // JvB for CANCEL: use invite CT in CANCEL request to get dialog 1367600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // (instead of stripping tag) 1368600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPClientTransaction ict = (SIPClientTransaction) lastRequest.getInviteTransaction(); 1369600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (ict != null) { 1370600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang dialog = ict.defaultDialog; 1371600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1372600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 1373600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang dialog = this.getDialog(dialogId); 1374600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1375600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1376600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // JvB: Check all conditions required for creating a new Dialog 1377600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (dialog == null) { 1378600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang int code = sipResponse.getStatusCode(); 1379600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if ((code > 100 && code < 300) 1380600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* skip 100 (may have a to tag */ 1381600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && (sipResponse.getToTag() != null || sipStack.isRfc2543Supported()) 1382600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && sipStack.isDialogCreated(method)) { 1383600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1384600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 1385600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Dialog cannot be found for the response. This must be a forked response. no 1386600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * dialog assigned to this response but a default dialog has been assigned. Note 1387600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * that if automatic dialog support is configured then a default dialog is always 1388600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * created. 1389600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1390600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1391600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang synchronized (this) { 1392600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 1393600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * We need synchronization here because two responses may compete for the 1394600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * default dialog simultaneously 1395600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1396600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (defaultDialog != null) { 1397600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipResponse.getFromTag() != null) { 1398600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPResponse dialogResponse = defaultDialog.getLastResponse(); 1399600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang String defaultDialogId = defaultDialog.getDialogId(); 1400600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (dialogResponse == null 1401600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang || (method.equals(Request.SUBSCRIBE) 1402600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && dialogResponse.getCSeq().getMethod().equals( 1403600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang Request.NOTIFY) && defaultDialogId 1404600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang .equals(dialogId))) { 1405600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // The default dialog has not been claimed yet. 1406600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang defaultDialog.setLastResponse(this, sipResponse); 1407600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang dialog = defaultDialog; 1408600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 1409600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 1410600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * check if we have created one previously (happens in the case of 1411600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * REINVITE processing. JvB: should not happen, this.defaultDialog 1412600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * should then get set in Dialog#sendRequest line 1662 1413600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1414600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1415600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang dialog = sipStack.getDialog(dialogId); 1416600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (dialog == null) { 1417600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (defaultDialog.isAssigned()) { 1418600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 1419600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Nop we dont have one. so go ahead and allocate a new 1420600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * one. 1421600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1422600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang dialog = sipStack.createDialog(this, sipResponse); 1423600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1424600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1425600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1426600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1427600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1428600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if ( dialog != null ) { 1429600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setDialog(dialog, dialog.getDialogId()); 1430600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 1431600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logError("dialog is unexpectedly null",new NullPointerException()); 1432600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1433600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 1434600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new RuntimeException("Response without from-tag"); 1435600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1436600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 1437600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // Need to create a new Dialog, this becomes default 1438600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // JvB: not sure if this ever gets executed 1439600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isAutomaticDialogSupportEnabled) { 1440600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang dialog = sipStack.createDialog(this, sipResponse); 1441600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.setDialog(dialog, dialog.getDialogId()); 1442600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1443600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1444600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } // synchronized 1445600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 1446600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang dialog = defaultDialog; 1447600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1448600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } else { 1449600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang dialog.setLastResponse(this, sipResponse); 1450600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1451600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.processResponse(sipResponse, incomingChannel, dialog); 1452600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1453600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1454600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 1455600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * (non-Javadoc) 1456600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 1457600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @see gov.nist.javax.sip.stack.SIPTransaction#getDialog() 1458600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1459600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public Dialog getDialog() { 1460600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang // This is for backwards compatibility. 1461600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang Dialog retval = null; 1462600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.lastResponse != null && this.lastResponse.getFromTag() != null 1463600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && this.lastResponse.getToTag() != null 1464600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang && this.lastResponse.getStatusCode() != 100) { 1465600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang String dialogId = this.lastResponse.getDialogId(false); 1466600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang retval = (Dialog) getDialog(dialogId); 1467600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1468600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1469600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (retval == null) { 1470600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang retval = (Dialog) this.defaultDialog; 1471600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1472600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1473600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) { 1474600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug( 1475600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang " sipDialogs = " + sipDialogs + " default dialog " + this.defaultDialog 1476600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang + " retval " + retval); 1477600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1478600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return retval; 1479600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1480600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1481600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1482600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 1483600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * (non-Javadoc) 1484600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 1485600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @see gov.nist.javax.sip.stack.SIPTransaction#setDialog(gov.nist.javax.sip.stack.SIPDialog, 1486600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * gov.nist.javax.sip.message.SIPMessage) 1487600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1488600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public SIPDialog getDialog(String dialogId) { 1489600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang SIPDialog retval = (SIPDialog) this.sipDialogs.get(dialogId); 1490600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return retval; 1491600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1492600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1493600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1494600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /* 1495600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * (non-Javadoc) 1496600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 1497600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @see gov.nist.javax.sip.stack.SIPTransaction#setDialog(gov.nist.javax.sip.stack.SIPDialog, 1498600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * gov.nist.javax.sip.message.SIPMessage) 1499600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1500600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public void setDialog(SIPDialog sipDialog, String dialogId) { 1501600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) 1502600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logDebug( 1503600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang "setDialog: " + dialogId + "sipDialog = " + sipDialog); 1504600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1505600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipDialog == null) { 1506600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (sipStack.isLoggingEnabled()) 1507600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang sipStack.getStackLogger().logError("NULL DIALOG!!"); 1508600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang throw new NullPointerException("bad dialog null"); 1509600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1510600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (this.defaultDialog == null) { 1511600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.defaultDialog = sipDialog; 1512600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if ( this.getMethod().equals(Request.INVITE) && this.getSIPStack().maxForkTime != 0) { 1513600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.getSIPStack().addForkedClientTransaction(this); 1514600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1515600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1516600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang if (dialogId != null && sipDialog.getDialogId() != null) { 1517600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.sipDialogs.put(dialogId, sipDialog); 1518600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1519600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1520600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1521600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1522600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1523600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public SIPDialog getDefaultDialog() { 1524600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return this.defaultDialog; 1525600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1526600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1527600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1528600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Set the next hop ( if it has already been computed). 1529600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 1530600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param hop -- the hop that has been previously computed. 1531600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1532600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public void setNextHop(Hop hop) { 1533600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.nextHop = hop; 1534600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1535600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1536600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1537600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1538600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Reeturn the previously computed next hop (avoid computing it twice). 1539600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 1540600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @return -- next hop previously computed. 1541600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1542600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public Hop getNextHop() { 1543600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return nextHop; 1544600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1545600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1546600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1547600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * Set this flag if you want your Listener to get Timeout.RETRANSMIT notifications each time a 1548600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * retransmission occurs. 1549600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * 1550600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @param notifyOnRetransmit the notifyOnRetransmit to set 1551600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1552600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public void setNotifyOnRetransmit(boolean notifyOnRetransmit) { 1553600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.notifyOnRetransmit = notifyOnRetransmit; 1554600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1555600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1556600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang /** 1557600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang * @return the notifyOnRetransmit 1558600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang */ 1559600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public boolean isNotifyOnRetransmit() { 1560600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang return notifyOnRetransmit; 1561600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1562600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1563600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang public void alertIfStillInCallingStateBy(int count) { 1564600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.timeoutIfStillInCallingState = true; 1565600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang this.callingStateTimeoutCount = count; 1566600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang } 1567600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1568600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1569600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang 1570600c7a4bbc7348167293eac928192e695b4ad5baChung-yih Wang} 1571