วันพุธที่ 25 กรกฎาคม พ.ศ. 2555

SIP programming for the Java developer


Deliver SIP-based services to Java applications with SIP Servlet

Session Initiation Protocol (SIP) is a control (signaling) protocol developed by the Internet Engineering Task Force (IETF) to manage interactive multimedia IP sessions including IP telephony, presence, and instant messaging. The SIP Servlet Specification (Java Specification Request 116), developed through the Java Community Process, provides a standard Java API programming model for delivering SIP-based services. Derived from the popular Java servlet architecture of Java Platform, Enterprise Edition (Java EE is Sun's new name for J2EE), SIP Servlet brings Internet application development capabilities to SIP solutions.
IT and telecom are converging. Network-IT applications, typically data oriented, are merging with communication applications. The increasing number of Call Me buttons appearing on Webpages is an example of this integration. The SIP Servlet Specification brings a familiar programming model to Java developers for building converged applications. This article gives a step-by-step introduction on how to use SIP Servlet to build a simple echo chat service.

Session Initiation Protocol

Defined in Request for Comments 3261, SIP is a protocol for establishing, modifying, and terminating multimedia IP communication sessions. Figure 1 is a simple example of using SIP to establish a VoIP (voice-over Internet Protocol) call:





Figure 1. Typical SIP message flow in VoIP calls

All the white lines in Figure 1 represent the SIP communications. Caller sends a SIP INVITE request to invite the "callee" to establish a voice session. Callee first responds with a message that has a 180 status code to indicate the phone is ringing. As soon as phone is picked up, a response with a 200 status code is sent to the caller to accept the invitation. Caller confirms with an ACK message, and session is established. Once the session is established, the actual digitized voice conversation typically transmits via Realtime Transmission Protocol (RTP) with the session, as the red line in Figure 1 indicates. When the conversation ends, a SIP BYE request is sent, followed by a response with a 200 status code to confirm the session termination.
Here is an example of a SIP INVITE request and a response with a 200 OK status code:
 SIP INVITE request:
INVITE sip:callee@callee.com SIP/2.0
Via: SIP/2.0/UDP pc.caller.com;branch=z9hG4bK776asdhds
Max-Forwards: 70
To: Callee <sip:callee@callee.com>
From: Caller <sip:caler@caller.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:caller@pc.caller.com>
Content-Type: application/sdp
Content-Length: 142


(content (SDP) is not shown)


SIP 200 OK response:
 SIP/2.0 200 OK
Via: SIP/2.0/UDP pc.caller.com;branch=z9hG4bK776asdhds;received=192.0.2.1
To: Callee <sip:callee@callee.com>;tag=a6c85cf
From: Caller <sip:caller@caller.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:callee@workstation.callee.com>
Content-Type: application/sdp
Content-Length: 131


(content (SDP) is not shown)


As you can see, the format of SIP resembles HTTP. However, when compared to HTTP, SIP is:

  • Responsible for session management. The actual multimedia content, such as instant messages, voice, and video, may or may not be transmitted via SIP.
  • Asynchronous and stateful. For each SIP request, there could be multiple responses. This means the application has to process each SIP message within a proper state context.
  • An application protocol that can run on both reliable and unreliable transport. Thus, the application must guarantee the message delivery with message retransmission and acknowledgement.
  • A peer-to-peer protocol where there is no clear distinction between client and server. Either side must be able to send and receive requests and responses.


SIP-based services

SIP-based services are SIP servers that offer services, such as message routing, to SIP endpoints, such as IP phones. For example, in Figure 2, the SIP registrar server and proxy server offer SIP registration and proxy services to help the SIP endpoints locate and communicate with each other.





message routing with SIP registrar and proxy server. Click on thumbnail to view full-sized image.

Figure 2 illustrates the following:
  1. Callee registers itself to the registrar server by sending a REGISTER request.
  2. The registrar server accepts the registration, which contains the callee's name address, by responding with a 200 OK status code.
  3. Caller requests to establish a communication session with callee by sending an INVITE request to the proxy server. The INVITE message's content typically contains the description of the communication session the caller wants to establish, such as media type, security, or IP address. The description is typically in Session Description Protocol (SDP) format.
  4. The proxy server looks up the registrar server to find out the callee's current address. Note that lookup is an implementation issue not part of SIP.
  5. The proxy server forwards the INVITE request from caller to callee based on its current address.
  6. Callee accepts the invitation by responding with a 200 OK status code. The 200 OK response to an INVITE request typically contains the description of the communication session that callee can establish with the caller.
  7. The proxy server forwards a 200 OK response from callee to caller.
  8. Caller confirms the session establishment by sending an ACK message to the proxy server. The ACK message may contain the final agreement on the session.
  9. In turn, the proxy server forwards the ACK to the callee. Thus, the three-way handshake is completed via the proxy server, and a session is established.
  10. Now the communication between caller and callee happens. The protocol used for communication may or may not be SIP. For example, instant messages can be transmitted over SIP. Voice conversations are typically transmitted over RTP.
  11. Now, callee finishes the conversation and wishes to terminate the session by sending a BYE request.
  12. Caller responds with a 200 OK status code to accept session termination.


In the above scenario, the SIP proxy server simply routes the messages to the callee's current address. As you can imagine, more interesting and smart routing services can happen. For example, the proxy server can "follow a user" by routing the messages to where he can be reached, such as a cell phone, even if someone is calling on his office phone.

SIP Servlet

Defined in Java Specification Request 116, the SIP Servlet Specification provides a container-servlet programming model for SIP applications. Since it is derived from the Java servlet architecture in Java EE, JSR 116 brings a familiar approach to building SIP services to Java EE developers.
The table below summarizes the similarity between HTTPServlet andSIPServlet.
Comparison between an HTTP and SIP servlet
HTTPSIP
Servlet classHttpServletSipServlet
SessionHttpSessionSipSession
Application packageWARSAR
Deployment descriptorweb.xmlsip.xml


Much like HTTP servlets, SIP servlets extend thejavax.servlet.sip.SipServlet class, which in turn extends thejavax.servlet.GenericServlet class. As you might have guessed,SipServlet overrides the service(ServletRequest request, ServletResponse response) method to handle different types of SIP messages.
Since SIP is asynchronous, only one of the request and response arguments in the service() method is valid; the other one is null. For example, if the incoming SIP message is a request, only the request is valid and the response is null, and vice versa. The default implementation of the SipServlet class dispatches requests todoXXX() methods and responses to doXXXResponse() methods with a single argument. For example, doInvite(SipServletRequest request) for a SIP invite request anddoSuccessResponse(SipServletResponse response) for SIP 2xx class responses. Typically SIP servlets override doXXX() methods and/ordoXXXResponse() methods to provide application logic.
How do you send SIP responses if there is no response object in thedoXXX() methods? In SIP servlets, you must call one of thecreateResponse() methods in thejavax.servlet.sip.SipServletRequest class to create a response object. Then, call the send() method on the response object to send the response.
How about creating a SIP request in a SIP servlet? There are two ways to create SIP requests: Call either one of the createRequest()methods on the SipSession class to create a SIP request within the session, or one of the createRequest() methods onjavax.servlet.sip.SipFactory to create a SIP request within a new SipSession. To get an instance of SipFactory, you must call getAttribute("javax.servlet.sip.SipFactory") on the ServletContext class.
The SipFactory is a factory interface in the SIP Servlet API for creating various API abstractions, such as requests, address objects, and application sessions. One interesting object created by SipFactory isjavax.servlet.sip.SipApplicationSession. The intention of JSR 116 is to create a unified servlet container that can run both an HTTP and a SIP servlet. SipApplicationSession provides a protocol-agnostic session object to store application data and correlate protocol-specific sessions, such as SipSession and HttpSession. Hopefully this concept will be adopted by future versions of the Servlet API to make it javax.servlet.ApplicationSession instead ofjavax.servlet.sip.SipApplicationSession.
The SipApplicationSession manages protocol-specific sessions like SipSession. The SipSession interface represents the point-to-point relationship between two SIP endpoints and roughly corresponds to a SIP dialog defined in Request for Comments 3261. SipSession is inherently more complicated than its HTTP counterpart due to SIP's asynchronous and unreliable nature mentioned above. For example, Figure 3 shows the SipSession state transitions defined in JSR 116:






Figure 3. State transitions in SipSession

Typically, an HttpSession is created when a user logs in and destroyed after logout. A SipSession typically represents one logical conversation, even if you have multiple conversations between the same endpoints. So SipSession is more dynamic and has a shorter lifespan.
More advanced discussions of the SipSession lifecycle and its relationship with SIP dialog reaches beyond this article's scope. Fortunately, the container handles most of the complexity, such as lifecycle and state transitions, and SipSession can simply be used as storage for session data.

A complete example: EchoServlet

The EchoServlet is a SIP servlet that can echo the instant messages you type in Windows Messenger:
 package com.micromethod.sample;


import java.io.IOException;
import java.util.HashMap;


import javax.servlet.*;
import javax.servlet.sip.*;


/**
 * EchoServlet provides a simple example of a SIP servlet.
 * EchoServlet echoes instant messages sent by Windows Messenger.
 */
public class EchoServlet extends SipServlet {


  /**
   * _address keeps the mapping between sign-in name and actual contact address.
   */
  protected HashMap _addresses = new HashMap();


  /**
   * Invoked for SIP INVITE requests, which are sent by Windows Messenger to establish a chat session.
   */
  protected void doInvite(SipServletRequest req) throws IOException, ServletException {
    // We accept invitation for a new session by returning 200 OK response.
    req.createResponse(SipServletResponse.SC_OK).send();
  }


  /**
   * Invoked for SIP REGISTER requests, which are sent by Windows Messenger for sign-in and sign-off.
   */
  protected void doRegister(SipServletRequest req) throws IOException, ServletException {
    String aor = req.getFrom().getURI().toString().toLowerCase();
    synchronized (_addresses) {
      // The non-zero value of Expires header indicates a sign-in.
      if (req.getExpires() != 0) {
        // Keep the name/address mapping.
        _addresses.put(aor, req.getAddressHeader("Contact").getURI());
      }
      // The zero value of Expires header indicates a sign-off.
      else {
        // Remove the name/address mapping.
        _addresses.remove(aor);
      }
    }
    // We accept the sign-in or sign-off by returning 200 OK response.
    req.createResponse(SipServletResponse.SC_OK).send();
  }


  /**
   * Invoked for SIP MESSAGE requests, which are sent by Windows Messenger for instant messages.
   */
  protected void doMessage(SipServletRequest req) throws IOException, ServletException {
    SipURI uri = null;
    synchronized (_addresses) {
      // Get the previous registered address for the sender.
      uri = (SipURI) _addresses.get(req.getFrom().getURI().toString().toLowerCase());
    }
    if (uri == null) {
      // Reject the message if it is not from a registered user.
      req.createResponse(SipServletResponse.SC_FORBIDDEN).send();
      return;
    }


    // We accept the instant message by returning 200 OK response.
    req.createResponse(SipServletResponse.SC_OK).send();


    // Create an echo SIP MESSAGE request with the same content.
    SipServletRequest echo = req.getSession().createRequest("MESSAGE");
    String charset = req.getCharacterEncoding();
    if (charset != null) {
      echo.setCharacterEncoding(charset);
    }
    echo.setRequestURI(uri);
    echo.setContent(req.getContent(), req.getContentType());
    // Send the echo MESSAGE request back to Windows Messenger.
    echo.send();
  }


  /**
   * Invoked for SIP 2xx class responses.
   */
  protected void doSuccessResponse(SipServletResponse resp) throws IOException, ServletException {
    // Print out when the echo message was accepted.
    if (resp.getMethod().equalsIgnoreCase("MESSAGE")) {
       System.out.println("\"" + resp.getRequest().getContent() + "\" was accepted: " + resp.getStatus());
    }
  }


  /**
   * Invoked for SIP 4xx-6xx class responses.
   */
  protected void doErrorResponse(SipServletResponse resp) throws IOException, ServletException {
    // Print out when the echo message was rejected/
    if (resp.getMethod().equalsIgnoreCase("MESSAGE")) {
       System.out.println("\"" + resp.getRequest().getContent() + "\" was rejected: " + resp.getStatus());
    }
  }


  /**
   * Invoked for SIP BYE requests, which are sent by Windows Messenger to terminate a chat session/
   */
  protected void doBye(SipServletRequest req) throws IOException, ServletException {
    // Accept session termination by returning 200 OK response.
    req.createResponse(SipServletResponse.SC_OK).send();
  }
}


Figure 4 shows how the SIP message flows between Windows Messenger and EchoServlet:





Figure 4. Message flow between Windows Messenger and EchoServlet

You can compile EchoServlet just like any Java code. Just make sure servlet.jar and sipservlet.jar (part of JSR 116 distribution) are on the classpath.
Similarly to HTTP servlet applications, each SIP servlet application has a deployment descriptor: sip.xml. Here is an example:
 <?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE sip-app 
  PUBLIC "-//Java Community Process//DTD SIP Application 1.0//EN" 
  "http://www.jcp.org/dtd/sip-app_1_0.dtd">


<sip-app>
  <display-name>SIP Servlet Sample</display-name>


  <servlet>
    <servlet-name>echo</servlet-name>
    <servlet-class>com.micromethod.sample.EchoServlet</servlet-class>
  </servlet>


  <servlet-mapping>
    <servlet-name>echo</servlet-name>
    <pattern>
      <or>
            <equal><var>request.method</var><value>REGISTER</value></equal>
            <equal><var>request.method</var><value>INVITE</value></equal>
      </or>
    </pattern>
  </servlet-mapping>
</sip-app>



The noticeable differences from a typical web.xml are the servlet mapping rules. Instead of specifying a URL pattern, you must write a Boolean expression in XML with various values in the SIP request headers. The SIP servlet container evaluates all the expressions from different applications for each initial SIP request—the SIP request does not belong to any existingSipSession. If an expression is evaluated to be true, the corresponding SIP servlet is invoked. If there are multiple matches, the container may simply invoke the first matching servlet or have other local policies.
In the above sip.xml, we simply specify that EchoServlet accepts the initial SIP REGISTER and INVITE requests. Once the session is established, all the messages sent within the session, such as MESSAGE and BYE, are automatically delivered to the servlet associated with the session.
Now, you must create a SIP servlet application package with EchoServlet and sip.xml in order to deploy and run. Similar to an HTTP servlet application, a SIP servlet application must be assembled in a SIP application archive (SAR). A SAR is simply a Java Archive (JAR) with additional requirements for the directory structure and content. The structure of SAR resembles WAR and should look something like the following:
  .../WEB-INF
      |-- classes
      |   |-- com
      |       |-- micromethod
      |           |-- sample
      |               |-- EchoServlet.class
      |-- sip.xml


You can simply create the SAR using the jar command from the top directory: jar cv0f echo.sar.

Deploying SAR in SIPMethod

Now it is time to deploy your SAR into your favorite SIP servlet container. Since each container has different deployment mechanisms, as an example, I use SIPMethod Application Server, a SIP servlet container from Micromethod Technologies.
Assuming you have properly downloaded and installed SIPMethod Application Server, to deploy a SAR into SIPMethod Application Server, you simply copy the SAR into SIPMethod's sipapps directory:
  • Linux: cp echo.sar <sm_home>/sipapps
  • Windows: copy echo.sar <sm_home>\sipapps
The <sm_home> is where you install SIPMethod Application Server.

Once SIPMethod starts, it will expand echo.sar into the echo directory under sipapps. Here is an example of the directory structure:
Figure 5. Directory structure after deployment

Test the EchoServlet

Assuming you have deployed echo.sar properly, you can start SIPMethod Application Server using its startup script:
  • Linux: <sm_home>/bin/startup.sh
  • Windows: <sm_home>\bin\startup.bat
                
EchoServlet requires Windows Messenger 5.0 or above. Please configure Windows Messenger with the following steps:
1. Open the Options dialog from the Tools menu
2. Open the Accounts tab in the Options dialog
3. Enable SIP Communications Service Account by checking "My contacts include users of a SIP Communications Service"
4. Type a name, such as weichen@micromethod.com, in the Sign-In Name field

Figure 6. Configure SIP account in Windows Messenger. Click on thumbnail to view full-sized image.

5. Open SIP Communications Service Connection Configuration dialog by clicking the Advanced... button
6. Click the Configure Settings radio button
7. Type IP address or machine name of where SIPMethod is running, such as 192.168.0.4, in Server Name or IP Address field
8. Select either TCP or UDP






Figure 7. Configure SIP connection in Windows Messenger. Click on thumbnail to view full-sized image.

Now make sure SIPMethod Application Server is running; sign into Windows Messenger with the sign-in account for the SIP Communications Service you entered in Options, such as weichen@micromethod.com. Now you can start a Send an Instant Message window by selecting the Actions menu, then Send an Instant Message... . Please select the Other tab and type in a name, such as anybody@yahoo.com, in the email address field. Then click OK.

Figure 8. Start a SIP chat session in Windows Messenger. Click on thumbnail to view full-sized image.

info : http://www.javaworld.com/javaworld/jw-06-2006/jw-0619-sip.html
http://opensbc.blogspot.com/2012/07/sip-programming-for-java-developer.html