This is an example of a click-to-dial application using Sip Servlets. Two users register softphones, and may call each other by clicking on a web site.
The example code is available for download here
.
This simple example shows how SIP Servlets can be used along with HTTP servlets to place calls from a web page. This example consists of the following steps:
Here are the SIP messages that are exchanged in this process:
![]() |
To handle these messages, we use two SIP Servlets and one HTTP Servlet. An overview of these servlets is given below.
The first servlet is a SIP Servlet that handles softphone registration. When Alice or Bob launches their softphone, it registers the softphone address with a SIP proxy. We will record the softphone contact information using Java Persistence API. It will be used later to call Alice or Bob.
Below is pseudocode for clicktodial.sip.RegistrarServlet. The full code can be found in the download bundle
.
/** A SIP Servlet to handle SIP REGISTER requests. */
public class RegistrarServlet extends SipServlet {
// inject the SipFactory
@Resource(mappedName="sip/ClickToDial")
private SipFactory sf;
/** Handle a SIP Register request. */
protected void doRegister(SipServletRequest req)
throws ServletException, IOException
{
int response;
// Figure out the name the user is registering with. This is the
// user portion of the SIP URI, e.g. "Bob" in "sip:Bob@x.y.z:port"
String username = ...
// get the Person object from the database
Person p = mf.getPerson(username);
if (p == null) {
// no person found in the database
response = SipServletResponse.SC_NOT_FOUND;
} else {
// the Expires header tells us if this is a registration or
// unregistration attempt.
int expires = Integer.parseInt(req.getHeader("Expires"));
if (expires == 0) {
// unregister
response = handleUnregister(req, p);
} else {
// register
response = handleRegister(req, p);
}
}
// send the response
SipServletResponse resp = req.createResponse(response);
resp.send();
}
/** Handle a registration request */
private int handleRegister(SipServletRequest req, Person p)
throws ServletException
{
// Get the contact address from the request. Prefer the
// "Contact" address if given, otherwise use the "To" address
Address addr = sf.createAddress(req.getHeader("Contact"));
// store the contact address in the database
p.setTelephone(addr.getURI().toString());
ModelFacade mf = (ModelFacade) getServletContext().getAttribute("Model");
mf.updatePerson(p);
return SipServletResponse.SC_OK;
}
}
The PlaceCall HTTP Servlet receives an HTTP post when Alice clicks on the "Call" link in her browser. It sends the initial SIP INVITE request to Alice and stores Bob's address in the SipSession. Below is pseudocode for clicktodial.web.PlaceCallServlet.
/** Place a phone call when a user clicks */
public class PlaceCallServlet extends HttpServlet {
// inject the SipFactory
@Resource(mappedName="sip/ClickToDial")
private SipFactory sf;
/** Processes requests for HTTP POST method. */
protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// get the source and target from the request
String sourceUserName = request.getParameter("source");
String targetUserName = request.getParameter("target");
// look up the relevant Person objects in the database
ModelFacade mf = (ModelFacade) getServletContext().getAttribute("Model");
Person source = mf.getPerson(sourceUserName);
Person target = mf.getPerson(targetUserName);
// get the phone numbers as SIP Addresses
SipApplicationSession appSession = sf.createApplicationSession();
Address to = sf.createAddress(source.getTelephone());
Address from = sf.createAddress(target.getTelephone());
// create an INVITE request to the first party from the second
SipServletRequest req = sf.createRequest(appSession, "INVITE", from, to);
// store the second party's address in the sip session
req.getSession().setAttribute("SecondPartyAddress", from);
// send the INVITE request
req.send();
}
}
The Call SIP servlet handles the OK response to the INVITE sent by the PlaceCall servlet above. Once an OK is received from Alice, this servlet sends an INVITE to Bob, specifying Alice's media preferences. When an OK is received from Bob, it sends an ACK to both parties. It also handle the BYE and call tear down.
Pseudocode for clicktodial.sip.CallSipServlet is below.
/** A SIP Serlvet that handles responses to click-to-dial invites
*/
public class CallSipServlet extends SipServlet implements SipSessionListener {
// inject the SipFactory
@Resource(mappedName="sip/ClickToDial")
private SipFactory sf;
/** Handle an OK response. */
protected void doSuccessResponse(SipServletResponse resp)
throws ServletException, IOException
{
SipSession session = resp.getSession();
// if the response was OK, invite the other party
if (resp.getStatus() == SipServletResponse.SC_OK) {
// we are in one of two states here. Either this OK is from
// the original invitee, meaning they answered and it is time
// to ring the second party, or the OK is from the second
// party, and we should acknowlege the call with both sides
// check for the first case, where this OK is from the original
// invitee
Address secondPartyAddress =
(Address) resp.getSession().getAttribute("SecondPartyAddress");
if (secondPartyAddress != null) {
// invite the second party
SipServletRequest invite = sf.createRequest(resp.getApplicationSession(),
"INVITE", session.getRemoteParty(), secondPartyAddress);
// get the SDP from the OK message
if (resp.getContentType().equals("application/sdp")) {
invite.setContent(resp.getContent(), "application/sdp");
}
// send the invite
invite.send();
} else {
// send acks
SipServletRequest secondPartyAck = resp.createAck();
SipServletRequest firstPartyAck =
(SipServletRequest) resp.getSession().getAttribute("FirstPartyAck");
// get the SDP from the second party's OK message
if (resp.getContentType().equals("application/sdp")) {
firstPartyAck.setContent(resp.getContent(), "application/sdp");
}
firstPartyAck.send();
secondPartyAck.send();
}
}
}
}
You will need to download and install SailFin first.
.
The example is distributed as a NetBeans project. You can build and deploy it directly from NetBeans 5.5.1
to a SailFin
container. You will also need to download install the SIP development plugin for NetBeans
.
After installing SailFin and NetBeans, you will register the SailFin server using Server Manager in NetBeans.
To open the sample in NetBeans:
Alternatively, you can skip NetBeans and use the ant scripts. There is a build.xml in the top level directory that will automatically build it and generate a deployable archive in dist/ClickToDial.war. You can deploy using asadmin deploy dist/ClickToDIal.war.
You can test this example using any SIP-based software phone. Instructions are provided for freely available X-Lite softphones: x-lite
In your web browser, open http://hostname:8080/ClickToDial
, where hostname is the name of the host where SailFin is running.
![]() |
Start off by logging in as Bob. Select 'Bob' in the drop-down menu and click Login. This should bring up Bob's home page, with no softphone registered:
![]() |
X-Lite is a freely available softphone for many platforms. To set up Bob's softphone, I used the settings below, where 129.148.173.187 is the IP address of my SailFin server. It is running on port 5060.
![]() |
With X-Lite setup, enable the account to perform the registration to SailFin server which is acting as a registrar. You should see a message like the following in the SailFin log:
Received register request: "Bob"<sip:Bob@192.168.2.103> Register address: "Bob"<sip:Bob@192.168.2.103:9990;transport=udp>;expires=3600
X-Lite also will show a message: You are logged in as Bob. Once X-Lite successfully registers, you can reload Bob's Login web page to get more information about his registration:
![]() |
On another computer, navigate to the login page and login as Alice.
After Alice's phone registers, you should be able to reload the page and see both softphone registered and ready:
![]() |
Now we are all set up to have Alice place a call to Bob (or Bob to Alice). When Alice clicks on the "Call" link next to Bob's name, her softphone should ring. When she picks up, Bob's sofphone should ring. When he answers, the call is connected. Either can disconnect first and the other is automatically disconnected by CallServlet.
See the SailFin
site as well as the Sip wiki page.