Friday Feb 29, 2008
Friday Feb 29, 2008
Session Initiation Protocol (SIP) is a signaling protocol that is used to set up, modify, and terminate a session between two endpoints. SIP can be used to set up a two-party call, a multi-party call, or even a multicast session for Internet calls, multimedia calls, and multimedia distribution. JSR 116: SIP Servlet API is a server-side interface describing a container of SIP components or services. SIP servlets, servlets that run in a SIP container, are similar to HTTP Servlets, but also support the SIP protocol. Together, SIP and SIP servlets, are behind many popular telecommunications-based applications that provide services such as Voice-over-IP (VoIP), instant messaging, presence and buddy list management, as well as web conferencing.
SIP and SIP servlets are also important in the enterprise. Combined with Java EE technology, SIP servlets can be used to add rich media interactions to enterprise applications. JSR 289: SIP Servlet v1.1 updates the SIP Servlet API and defines a standard application programming model to mix SIP servlets and Java EE components. SIP servlets are going to play an even bigger part in building the next generation of telecommunications services.
This Tech Tip covers some of the basic concepts underlying SIP and SIP servlets. It also presents a sample application that uses SIP servlets and HTTP servlets to provide VoIP phone service.
What is SIP?
An easy way to describe SIP is to examine a usage scenario. Let's say a user identified as A wants to set up a call with a user identified as B. In a telecommunications setting, user A and B would communicate through what are called user agents. One example of a user agent is a soft phone -- a software program for making telephone calls over the Internet. Another example is a VoIP Phone -- a phone that uses VoIP. Here are the steps that need to happen to set up the call:
Figure 1 illustrates the steps in setting up a call.
![]() |
| Figure 1. Steps in Setting Up a Call |
SIP provides a standardized way of carrying out these steps. It does this by defining specific request methods, responses, response codes, and headers for signaling and call control. The protocol has been standardized by the Internet Engineering Task Force (IETF) under RFC3261 and is now accepted as the standard signaling protocol for the 3rd Generation Partnership Project (3GPP) and as a permanent element in the IP Multimedia Subsystem (IMS) architecture.
How is SIP related to HTTP?
People often ask if SIP uses HTTP as the underlying protocol. The answer is no. SIP is a protocol that operates at the same layer as HTTP, that is, the application layer, and uses TCP, UDP, or SCTP as the underlying protocol. However, SIP does have a lot of similarities with HTTP. For example, like HTTP, SIP is text based and user readable. Also like HTTP, SIP uses a request-response mechanism with specific methods, response codes, and headers. A notable difference between HTTP and SIP is that the request-response mechanism is asynchronous in SIP -- a request does not need to be followed by a corresponding response. In fact, a SIP request could result in one or more requests being generated.
SIP is a peer-to-peer protocol. This means that a user agent can act as a server as well as a client. This is another difference between SIP and HTTP. In HTTP, a client is always a client and a server is always a server.
SIP supports the following request methods and response codes:
Request methods:
REGISTER. Used by a client to register an address
with a SIP server. .
INVITE. Indicates that the user or service is being
invited to participate in a session.
The body of this message includes a description of the session to which
the user or service is being invited.ACK. Confirms that the client has received a final
response to an INVITE request.
This method is only used with INVITE requests.CANCEL. Used to cancel a pending request.BYE. Sent by a user agent client to indicate to the
server that it wishes to terminate the call.OPTIONS. Used to query a server about its
capabilities.Response codes:
1xx: Provisional. An ACK that
indicates the action was successfully received, understood, and
accepted.3xx: Redirection. Further action is required to
process this request.4xx: Client Error. The request contains incorrect
syntax and cannot be fulfilled at this server.5xx: Server Error. The server failed to fulfill an
apparently valid request.6xx: Global Failure. The request cannot be fulfilled
at any server.Session Description Protocol
Session Description Protocol (SDP) is a format for describing the media format and type to be used in a multimedia session. SIP uses SDP as a payload in its messages to facilitate the exchange of capabilities between various user agents. For example, the content of an SDP might specify the codecs supported by the user agent and the protocol to be used such as Real-time Transport Protocol (RTP).
SIP Message
Figure 2 shows the composition of a SIP message . There are three major parts:
![]() |
| Figure 2. Composition of a SIP Message |
The SIP Servlet Model
The SIP servlet programming model is based on the servlet programming model. It brings programming in SIP closer to Java EE. Servlets are server-side objects that process incoming requests and send an appropriate response to the client. They are typically deployed in a servlet container and have a well-defined life cycle. The servlet container is responsible for managing the life cycle of the servlets within the container and managing resources related to technologies such as JNDI and JDBC that the servlet uses. The servlet container also manages network connections for servlets.
As mentioned earlier, SIP servlets are similar to HTTP Servlets, except
that they process SIP requests.
They do this by defining specific methods to process each of the SIP
request methods.
For example, HTTP servlets define the doPost() method,
which overrides the service() method,
to handle POST requests. By comparison, SIP servlets
define a doInvite() method, which also
overrides the service() method, to handle INVITE
requests.
JSR116 defined SIP Servlet API 1.0. It specified:
The initial SIP Servlet API specification is being revised by JSR 289: SIP Servlet v1.1.
SIP Servlet API -- Key Concepts
The key concepts that underlie SIP servlets are similar to those that underlie HTTP servlets. The following sections briefly describe some of those concepts.
SipServletRequest and SipServletResponse
The request-response methodology in SIP is similar to that for HTTP
servlets. A request is defined in a
SipServletRequest object and a response in a SipServletResponse
object.
However, only one ServletRequest or ServletResponse
object is non-null. That's because
a SIP request does not result in a symmetric response. There is also a
common super interface, called
SipServletMessage, for both SipServletRequest
and SipServletResponse objects.
The SipServletMessage interface defines the methods that
are common to SipServletRequest
and SipServletResponse objects.
Figure 3 illustrates the hierarchy of the SipServletRequest
and
SipServletResponse objects.
![]() |
Figure 3. Hierarchy of SipServletRequest
and SipServletResponse Objects
|
Servlet Context
The servlet context as defined in the servlet specification also applies to SIP servlets. The servlet specification defines specific context attributes that are used to store and retrieve information specific to SIP servlets and interfaces from the context. The servlet context can be shared with HTTP servlets within the same application. This is explained in the section Converged Applications.
Deployment Descriptor
An XML-based deployment descriptor is used to describe the SIP
servlets, the rules for invoking them, as well as
the resources and environment property used in the application. This
descriptor is in a sip.xml file and
is similar to the sip.xml
file is defined by an XML
schema.
SIP Application Packaging
SIP applications have the same packaging structure as web applications.
They are packaged in the JAR format with a file
extension of .sar (Sip archive) or .war (web archive).
Converged Context and Converged Application
An application may use both SIP and HTTP servlets to create a service.
To allow for HTTP and SIP servlets being in the same application
package, the SIP servlet specification
defines a ConvergedContext object. This object holds the
servlet context shared
by both HTTP and SIP servlets and provides the same view of the
application to SIP and HTTP servlets in terms of
servlet context attributes, resources, and JNDI namespaces.
When an application includes both SIP and HTTP servlets it is known as
a converged application. This is in contrast
to a SIP-only application, which is called a SIP application. A
converged application is similar in structure to
a SIP application except that it has a web.xml file as a
deployment descriptor in addition to
a sip.xml file. In SIP Servlet API 1.1 (JSR289), the
concept of converged applications has been extended
to also cover enterprise applications. Enterprise applications can now
include a SIP application or a converged application
as a module. This type of enterprise application is called a converged
enterprise application.
SIP Sessions
The SIP servlet specification defines SipSession objects
to represent a session over SIP in the same way as
HttpSession objects represent sessions over HTTP. Because
a single application such as a converged
application may have sessions over HTTP and SIP, the specification also
defines SipApplicationSession, which
is a session object at the application level. The SipApplicationSession
object acts as a parent to HTTP and
SIP sessions (that is, protocol sessions) in an application.
Annotations
Recall that the SIP Servlet API 1.1 aims to align SIP servlets with Java EE 5. As a result, the specification introduces the use of annotations defined by Java EE 5 within SIP servlets and listeners. It also defines custom annotations to represent interfaces defined by the SIP servlet specification. The specification introduces the following annotations:
@SipServlet. Used to indicate that a particular class is
a SipServlet@SipApplication. Used to define a SIP application. This
annotation has a set of attributes, one of them being the "name" attribute,
which is used to define the name of the application. The
SipApplication annotation can be used to create a logical collection of
servlets which form an application, without the use of deployment descriptor.@SipListener. Allows a particular class to be
registered as a SipListener for a particular SIP application. The name
of the SIP application is defined as an attribute to this annotation.@SipApplicationKey. A method level that helps define a
SipApplicationKey for a SIP application. The SipApplicationKey
is used to associate request with existing SipApplicationSessions.Project Sailfin - an Open Source SIP Application Server
A SIP servlet container can be standalone, that is, supporting only SIP servlets, or it can be a converged container supporting both HTTP and SIP servlets. However, for most enterprise uses, a SIP servlet container needs to be a converged container within an application server. Project Sailfin is an effort to produce an open source implementation of a SIP servlet container using the GlassFish application server. The project is being developed under java.net, with Sun and Ericsson as the major contributors. Sailfin, the SIP servlet container implementation in GlassFish being developed in the SailFin project, supports SIP Servlet API 1.0 and aims to support SIP Servlet API 1.1 when the specification is finalized.
The CallSetup Sample Application
The sample application for this tip, named CallSetup, is available as
part of the SailFin download. You can download
SailFin from the
Download SailFin Builds page.
Follow the
SailFin Project - Instructions
to install and configure SailFin. The code for the CallSetup
application is in the
<sailfin-install-home>/samples/sipservlet/CallSetup
directory, where
<sailfin-install-home> is the directory where you
installed SailFin.
The CallSetup application uses SIP servlets and HTTP servlets to provide VoIP phone service. The application places VoIP calls with the help of a Back-to-Back User Agent (B2BUA) SIP servlet. A B2BUA sets up a call between two user agents by calling each user agent individually and then connecting the two of them.
CallSetup Components
CallSetup includes the following components:
Registration.java. A plain old Java object (POJO)
that represents the registered user.RegistrarServlet. A SIP servlet that allows users to
register. The servlet is also used to persist
the user data in the the Java DB database bundled with SailFin.RegistrationBrowserServlet.java. An HTTP Servlet
that provides an interface to select the registered
users for the call.SipCallSetupServlet.java. An HTTP Servlet that sends
an INVITE message to the first user (UserB).web.xml. Deployment descriptor for the HTTP servlets.sip.xml. Deployment descriptor for the SIP servlets.sun-web.xml. A product-specific deployment
descriptor.persistence.xml. Defines the persistence unit.Figure 4 shows the sequence of execution of the application.
|
|
| Figure 4. CallSetup Sequence of Execution |
Let's look at some of the code in the components that comprise CallSetup. Not all of the components for the application are shown here and not necessarily all the code in each component. You're encouraged to explore the entire code for the application in the SailFin download.
RegistrarServlet.java
When a user agent sends a REGISTER request, the doRegister()
method is invoked and the
registration data is stored. A response with a status code is then sent
to the user agent.
| import
com.ericsson.sip.Registration; @PersistenceContext(name = "persistence/LogicalName", unitName = "EricssonSipPU") public class RegistrarServlet extends SipServlet{ The PersistenceUnit annotation is used to annotate the EntityManagerFactory with the name of the PU to be used. @PersistenceUnit(unitName = "EricssonSipPU") private EntityManagerFactory emf; The Resource annotation is used to inject the UserTransaction @Resource private UserTransaction utx; protected void doRegister(SipServletRequest request) throws ServletException, IOException { SipServletResponse response = request.createResponse(200); try { The SipServletRequest object is parsed to get to address and request headers. The Contact header is obtained and stored in the database SipURI to = cleanURI((SipURI) request.getTo().getURI()); ListIterator<Address> li = request.getAddressHeaders("Contact"); while (li.hasNext()){ Address na = li.next(); SipURI contact = (SipURI) na.getURI(); logger.log(Level.FINE, "Contact = " + contact); An EntityManager object is created for storing the user data. EntityManager em = emf.createEntityManager(); try { utx.begin(); Registration registration = new Registration(); registration.setId(to.toString()); registration = em.merge(registration); em.remove(registration); utx.commit(); logger.log(Level.FINE, "Registration was successfully created."); } catch (Exception ex) { try { utx.rollback(); } catch (Exception e) { } } em.close(); If the registration is successful , a response code of 200 OK is sent response.send(); } catch(Exception e) { If the registration is not successful , a response code of 500 is sent response.setStatus(500); response.send(); } } |
RegistrationBrowserServlet.java
This is an HTTP Servlet that provides an interface to list registered users and to set up calls between the two of them.
| @PersistenceContext(name
= "persistence/LogicalName", unitName =
"EricssonSipPU") public class RegistrationBrowserServlet extends HttpServlet { @PersistenceUnit(unitName = "EricssonSipPU") private EntityManagerFactory emf; public Collection getRegistrations() { EntityManager em = emf.createEntityManager(); Query q = em.createQuery("select object(o) from Registration as o"); return q.getResultList(); } protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); This gets the list of registrations Collection registrations = getRegistrations(); Iterator iter = registrations.iterator(); out.println("<html><body>"); Call to the HTTP servlet SipCallSetupServlet out.println("<FORM ACTION = \"/CallSetup/SipCallsetupServlet\" METHOD = POST>"); out.println("<INPUT TYPE=SUBMIT NAME=Submit VALUE=\"Submit\">"); out.println("</FORM>"); out.println("SipFactoryInstance = "+sf.toString()); out.println("</body></html>"); out.close(); } |
SipCallSetupServlet.java
This HTTP Servlet is called from the RegistrationBrowserServlet
and
acts like a B2BUA by setting up a call between the two users.
| public
class SipCallSetupServlet extends HttpServlet { SipFactory sf = null; TimerService ts = null; ServletContext ctx = null; public void init(ServletConfig config) throws ServletException { ctx = config.getServletContext(); Getting the SIpFactory object from the ServletContext sf = (SipFactory) ctx.getAttribute(SipServlet.SIP_FACTORY); } protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String callA = null; String callB = null; Getting the contacts from request parameters String[] contacts = request.getParameterValues("CONTACT"); if ( contacts.length < 2 ) { return; } callA = contacts[0]; callB = contacts[1]; Using the SipFactory object to create a SipApplicationSession SipApplicationSession as = sf.createApplicationSession(); Using the SipFactory object to create a "To" and "From" Address objects Address to = sf.createAddress(callB); Address from = sf.createAddress(callA); Creating a SipServletRequest using the SipFactory and the SipApplicationSessionObjects created. Note that INVITE is being sent as if UserA is inviting UserB. SipServletRequest sipReq = sf.createRequest(as, "INVITE", from, to); logger.log(Level.FINE, "SipCallSetupServlet sipRequest = " + sipReq.toString()); Set an attribute in SipServletRequest to indicate that this is an initial INVITE sipReq.setAttribute("CALL","INITIAL"); // set servlet to invoke by response Getting a SipSession from the Request created SipSession s = sipReq.getSession(); This is the key part. We set name of the servlet that would handler the response for the Request being sent. Here b2b is the name of the SIP Servlet that would handle the response to this request. s.setHandler("b2b"); // lets send invite to B ... Sending the request sipReq.send(); } |
B2BCallServlet.java
This SIP Servlet receives and handles the response to the INVITE
request sent by SipCallSetupServlet.
The servlet processes the response headers and body, gets the SDP, and
sends another INVITE request to the
other user agent, along with the SDP metadata. After it receives the
response from other user with a success response
code, the servlet sets up the call between the two users.
| public
class B2BCallServlet extends SipServlet { SipFactory sf = null; ServletContext ctx = null; public void init(ServletConfig config) throws ServletException { super.init(config); ctx = config.getServletContext(); Get the SipFactory from the ServletContext sf = (SipFactory) ctx.getAttribute(SipServlet.SIP_FACTORY); ts = (TimerService) ctx.getAttribute(SipServlet.TIMER_SERVICE); } protected void doResponse(SipServletResponse resp) throws ServletException, IOException { get the SipApplicationSession and SipServletRequest from the response SipApplicationSession sas = resp.getApplicationSession(true); SipServletRequest origReq = resp.getRequest(); String alreadySent = (String) origReq.getAttribute("SENT_INVITE"); if( alreadySent == null && resp.getContentLength() > 0 && resp.getContentType().equalsIgnoreCase("application/sdp")) { String responseFrom = (String) origReq.getAttribute("CALL"); Check if this an response to INITIAL INVITE sent from the HTTP Servlet, and if there is an SDP sent in the response, create an INVITE to the other user if("INITIAL".equals(responseFrom)) { //Take the SDP and send to A Note that To address in the orginal request is the From address here and vice versa. This is what makes this servlet act like a B2BUA. SipServletRequest newReq = sf.createRequest(sas,"INVITE",origReq.getTo(),origReq.getFrom()); newReq.setContent(resp.getContent(),resp.getContentType()); SipSession ssA = newReq.getSession(true); SipSession ssB = resp.getSession(true); Set the SipSession object as a session attribute to each call leg ssA.setAttribute("OTHER_SESSION",ssB); ssB.setAttribute("OTHER_SESSION",ssA); //Test Set the b2b servlet as the handler for the response for the new request being sent. ssA.setHandler("b2b"); ssB.setHandler("b2b"); origReq.setAttribute("SENT_INVITE","SENT_INVITE"); send the request to the other user newReq.send(); //Send to A } else { If this is a response from User A then get the SDP from User A and set it i SipSession ssB = (SipSession) resp.getSession().getAttribute("OTHER_SESSION"); ssB.setAttribute("SDP",resp.getContent()); } } else { return; } // Count so that both sides sent 200. If response has a 200OK as the status code if( resp.getStatus() == 200 ) { Check if this is a response from the UserB ( first user) SipServletResponse first = (SipServletResponse) sas.getAttribute("GOT_FIRST_200"); if( first == null ) { // This is the first 200 sas.setAttribute("GOT_FIRST_200",resp); } else { //This is the second 200 sen both ACK This is a second response and now we send an ACK to both User A and UserB. This exchanges the SDP and sets up the call. sendAck(resp); sendAck(first); } } } This method sends the ACK with the SDP. private void sendAck( SipServletResponse resp ) throws IOException { SipServletRequest ack = resp.createAck(); //Check if pending SDP to include in ACK Object content = resp.getSession().getAttribute("SDP"); if( content != null ) { ack.setContent(content,"application/sdp"); } ack.send(); } } |
sip.xml
The sip.xml file defines the SIP servlets and specifies
their mapping. The mapping for a SIP servlet
uses the equal, and, or, and not
operators to define a condition
in which the servlet is invoked. In this case, the match is on the
request method being either REGISTER,
INVITE, OPTIONS, or MESSAGE.
| <sip-app> <display-name>SIP Registrar</display-name> <description>SIP Registrar application</description> <servlet> <servlet-name>registrar</servlet-name> <description>Registrar SIP servlet</description> <servlet-class>com.ericsson.sip.RegistrarServlet</servlet-class> <init-param> <param-name>Registrar_Domain</param-name> <param-value>ericsson.com</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet> <servlet-name>b2b</servlet-name> <servlet-class>com.ericsson.sip.B2BCallServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>registrar</servlet-name> <pattern> <and> <equal> <var ignore-case="false">request.uri.host</var> <value>test.com</value> </equal> <or> <equal> <var ignore-case="false">request.method</var> <value>REGISTER</value> </equal> <equal> <var ignore-case="false">request.method</var> <value>INVITE</value> </equal> <equal> <var ignore-case="false">request.method</var> <value>OPTIONS</value> </equal> <equal> <var ignore-case="false">request.method</var> <value>MESSAGE</value> </equal> </or> </and> </pattern> </servlet-mapping> </sip-app> |
persistence.xml
The persistence.xml file defines the persistence unit, EricssonSipPU,
which is used to persist
the registration data in the database. The application uses the default
JDBC resource available with Sailfin,
jdbc/__default.
| <?xml
version="1.0" encoding="UTF-8"?> <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> <persistence-unit name="EricssonSipPU" transaction-type="JTA"> <provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider> <jta-data-source>jdbc/__default</jta-data-source> <properties> <property name="toplink.ddl-generation" value="drop-and-create-tables"/> </properties> </persistence-unit> </persistence> |
Running the Sample Application
To run the CallSetup application, follow the instructions on the SailFin Project - Instructions page. If you follow the instructions successfully, you should be able to set up a call between two softphone clients. The phone should ring sequentially at two selected end points.
Summary
This tip covered some of the basic concepts underlying SIP and SIP servlets. It also presented a sample application that uses SIP servlets and HTTP servlets to provide VoIP phone service. And it introduced the SailFin project, which is building an open source implementation of a SIP servlet container using the GlassFish application server.
To learn more about SIP, SIP servlets, and the SailFin project, consult the following resources:
Note that there is a NetBeans plugin available for SIP servlets, which allows you to create a SIP application using the NetBeans IDE. The plugin is available as a part of SailFin. You can learn more about the NetBeans plugin in the blog entry NetBeans 6.1 and Project Sailfin.
About the Author
Prasad Subramanian is a staff engineer at Sun Microsystems and is the engineering lead for Project Sailfin.
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
Connect
and Participate With GlassFish
Try GlassFish for a chance to win an iPhone. This sweepstakes ends on
March 23, 2008. Submit your entry today.
Great article!
Does this work only within an enterprise network? I'd like to have a Sailfin server running at my colo datacenter, and have home users use it to do voice conf / IM. Are there issues getting past home routers when the two users try to connect, or has that been solved somehow in the code?
Posted by bob on March 05, 2008 at 02:15 PM PST #
At this time Sailfin works only within an enterprise network. To make home users connect to Sailfin directly via home routers, we may need a way to work around NAT issues. That is something that we want to solve in the next quarter. Stay tuned !
Try and listen to users@sailfin.dev.java.net AND dev@sailfin.dev.java.net
Thanks for asking !
--Prasad
Posted by Prasad Subramanian on March 11, 2008 at 11:39 PM PDT #
Great work and great introduction,
thanks Prasad. I have been waiting for the package for a long time since the birth of SIP/SDP, for building instant messaging, presence and buddy list management, as well as web conferencing into a J2EE application server and product which is installed and setting up in a personal desktop or laptop, either for personal content publishing and management. As regard to NAT issue, you always need a central server to hook up two users.
thanks Prasad.
Posted by Fan on April 07, 2008 at 03:44 PM PDT #
Hello,
Which features do you plan to make sailfin working with NAT home routers ?
I plan to make a demo installation of a sailfin server and this would also require access through NAT routers. In my opinion NAT issues can be solved on client side and would not require server side changes.
Posted by Peter Klein on April 16, 2008 at 03:42 AM PDT #
@Fan : Thanks ! Yes, we would need a STUN server to resolve NAT issues. There is a STUN server that is available with jVoiceBridge, from http://jvoicebridge.dev.java.net , which could be a potential integration.
@Peter : We would need to potentially change Sailfin to listen to both SIP and STUN requests on the same port. This is still under discussion. Do you have any ideas on this ?
Thanks
Prasad
Posted by Prasad Subramanian on April 16, 2008 at 10:20 AM PDT #
Hi,
We have a custom SIP Stack developed using C. And they expose dll's for the SIP events. We need to develop a browser based IM client for voice chat using the SIP stack.
Is it possible to use SIPServlet?
Thanks in advance
Java_User
Posted by Java User on September 30, 2008 at 02:57 AM PDT #
Hi Prasad,
Great introduction to Sailfin.
Are there any plans on adding the "voice" part of the signalling into the Sailfin project? Eg. the recent JSR 309 or like?
Thanks,
Soren
Posted by Soren on October 24, 2008 at 09:51 AM PDT #
how can i communicate a sip-servlet with a http-servlet?
Posted by diego gallego on September 11, 2009 at 08:18 PM PDT #