Automatic invalidation of SIP sessions (aka Invalidate When Ready - IWR) in SailFin
This blog explains how the automatic invalidation of SIP sessions work in SailFin container.To give some brief introduction, following are the ways by which a SIP session can be invalidated in SailFin container: 1. Explicit invalidation by the application itself i.e., by invoking session.invalidate(). 2. SAS (SipApplicationSession) expiration which eventually invalidates SAS and all its children, if the application does not extend the SAS expirationTime in sessionExpired listener callback. 3. IWR i.e., Automatic invalidation of sessions, I would call it "container managed invalidation" which is the fastest invalidation and resource cleanup machanism. This type of invalidation happens as soon as the sip dialog terminates (eg., after processing BYE request). In the rest of the blog I will explain more about how this particular invalidation machanism works and the how the applications can use it and get benefit out of it. Here are the details of automatic invalidation (IWR): Section #1. Enabling the automatic invalidation: (a) If the application is of SSA 1.1 version, then the automatic invalidation is enabled by default. The 1.1 application will have its sip.xml like this: [prettifyclass="prompt"]<?xml version="1.0"?><br /><sip-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br /> xsi:schemaLocation="http://www.jcp.org/xml/ns/sipservlet http://www.jcp.org/xml/ns/sipservlet/sip-app_1_1.xsd"<br /> version="1.1"><br />.....<br /></sip-app>[/prettify]It is also possible to write 1.1 applications only using annotations without requiring sip.xml (b) If the application is of SSA 1.0 version,, then the automatic invalidation can be enabled programatically. This need to be done when processing the initial request, something like this: [prettifyclass="prompt"]doInvite/doSubscribe/doRefer(SipServletRequest req) { // initial request.<br /> req.getApplicationSession().setInvalidateWhenReady(true);<br /> req.getSession().setInvalidateWhenReady(true);<br />}[/prettify]Section #2. Once enabled, the automatic invalidation of javax.servlet.sip.SipSession takes place under following circumstances: (a) After BYE is processed. From a application point of view, it looks something like this: [prettifyclass="prompt"]<span style="color: rgb(51, 51, 51);">public class UASServlet { // case 1 : UAS</span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> doBye(SipServletRequest req) { // incoming BYE request.</span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> SipSession s = req.getSession();</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> s.isReadyToInvalidate(); // would be false;</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> s.isValid(); // would be true</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> SipServletResponse resp = s.createResponse(<span style="color: rgb(0, 153, 0);"><resp-code></span>);</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> resp.send();</span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> <span style="color: rgb(255, 0, 0);">// After the resp.send(), the SipSession is no longer valid in all cases,<br /> // except when <span style="color: rgb(0, 153, 0);"><resp-code></span>=401/407/408.</span></span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(204, 51, 204);"> // when <span style="color: rgb(0, 153, 0);"><resp-code></span>=401/407/408 the client might resend the BYE again, </span><br style="color: rgb(204, 51, 204);" /><span style="color: rgb(204, 51, 204);"> // so we need to retain the SipSession.</span><br style="color: rgb(204, 51, 204);" /><span style="color: rgb(51, 51, 51);"> }</span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);">} </span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);">public class UACServlet { // case 2 : UAC</span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> doResponse(SipServletResponse resp) { </span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(0, 153, 0);"> if (resp.getRequest().getMethod().equals("BYE")) { </span>// repsonse to BYE.<br /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> SipSession s = resp.getSession();</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> s.isValid(); // would be true;</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> s.isReadyToInvalidate(); // would be true;</span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(255, 0, 0);"> // The SipSession would be invalidated immediately after this method finishes, </span><br style="color: rgb(255, 0, 0);" /><span style="color: rgb(255, 0, 0);"> // in all cases except when resp.getStatus() is 401/407/408.</span><br style="color: rgb(255, 0, 0);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(204, 51, 204);"> // when resp.getStatus()=401/407/408, we can send BYE again and expect a response, </span><br style="color: rgb(204, 51, 204);" /><span style="color: rgb(204, 51, 204);"> // so we need to retain the SipSession.</span><br style="color: rgb(153, 51, 153);" /><span style="color: rgb(51, 51, 51);"> }</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> }</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);">}</span><br style="color: rgb(51, 51, 51);" /><br />[/prettify](b) After NOTIFY is processed whose Subscription-State=terminated. This is what it means for an application: (c) When the non-record-routing proxy finishes proxying the request. Like below: [prettifyclass="prompt"]<span style="color: rgb(51, 51, 51);">class NonRecordRoutingProxy {</span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> protected void doInvite(SipServletRequest req)</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> throws ServletException, IOException {</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> SipSession s = req.getSession();</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> if (req.isInitial()) {</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> Proxy proxy = req.getProxy();</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(0, 153, 0);"> proxy.setRecordRoute(false);</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> proxy.setSupervised(true);</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> proxy.proxyTo(req.getRequestURI());</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> }</span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(255, 0, 0);"> // After this method returns, SipSession is invalidated immediately.</span><br style="color: rgb(255, 0, 0);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> }</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);">}</span><br />[/prettify](d) Non 2xx final response is received for the initial request. [prettifyclass="prompt"]<span style="color: rgb(51, 51, 51);">class UACServlet { // we are the Caller.</span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> doErrorResponse(SipServletResponse resp) {</span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> SipServletRequest req = resp.getRequest();</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> int statusCode = resp.getStatus();<br /><br style="color: rgb(51, 51, 51);" /></span><span style="color: rgb(0, 153, 0);"> if ((req == INVITE / SUBSCRIBE / REFER) && (300 <= status <= 699)) { // error response for INITIAL request.</span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(255, 0, 0);"> // The session will be invalidated immediately after this method returns.</span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> }</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);"> }</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);">} </span><br style="color: rgb(51, 51, 51);" /><br />[/prettify]
That is about the SipSession' invalidation in the simplest possible terms. Now let us see when javax.servlet.sip.SipApplicationSession gets automatically invalidated. For the SipApplicationSession it is very simple : Automatic invalidation happens when the SipApplicationSession becomes completely EMPTY i.e., when all its child protocol sessions (sip & http) get invalidated & all its servlettimers expire. Section #3. Controlling the automatic invalidation: When the session is about to be automatically invalidated, sessionReadyToInvalidate(...) callback is invoked in the listener. In this callback, the application can decide whether it should allow the automatic invalidation or not. For example: [prettifyclass="prompt"]<span style="color: rgb(51, 51, 51);">class SipSessionListenerImpl implements SipSessionListener {<br /><br style="color: rgb(51, 51, 51);" /></span><span style="color: rgb(51, 51, 51);"> sessionReadyToInvalidate(SipSessionEvent e) {<br /><br style="color: rgb(51, 51, 51);" /></span><span style="color: rgb(51, 51, 51);"> SipSession s = e.getSession();</span><br style="color: rgb(51, 51, 51);" /><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(0, 153, 0);"> // invoke s.setInvalidateWhenReady(false) to disallow automatic invalidation </span><br style="color: rgb(0, 153, 0);" /><span style="color: rgb(0, 153, 0);"> // i.e., SipSession will NOT be invalidated after this method returns.</span><br style="color: rgb(0, 153, 0);" /><br style="color: rgb(204, 51, 204);" /><span style="color: rgb(204, 51, 204);"> // do nothing, to allow the automatic invalidation </span><br style="color: rgb(204, 51, 204);" /><span style="color: rgb(204, 51, 204);"> // i.e, the SipSession will be invalidated when this method returns.</span><br style="color: rgb(204, 51, 204);" /><span style="color: rgb(51, 51, 51);"> }</span><br style="color: rgb(51, 51, 51);" /><span style="color: rgb(51, 51, 51);">}</span><br />[/prettify]
Similar thing can be done in the SipApplicationSessionListener as well. Also, if you don't implement any of these listeners, then the container assumes that the automatic invalidation is allowed. Thats a very quick and most simplified description of IWR. |
- Printer-friendly version
- bhavanishankar's blog
- 2000 reads





