Introduction to the SIP API for Java MEby Emmanuel Proulx This tutorial provides an easy-to-follow approach to developing a Java ME application that uses SIP. Along the way, it examines the SIP API for Java ME (JSR 180), which is distributed with the Java Wireless Toolkit. It also explores the various ways in which this stack can be used. You will see a real SIP application running on a cell phone or emulator. Introduction: SIP + Java = The Way To GoMobile phones and Internet-ready PDAs are more popular than ever. All my friends have them. They bring with them a giant bouquet of possibilities for new applications. Many of these will be "networked," either client/server or point-to-point. When you develop a mobile networked application, you have a choice of communication protocol. You could open sockets and invent a completely proprietary protocol. You could use SOAP but with a proprietary API. Or you could go with a completely standards-based approach. I suggest using the latter, for the following reasons:
This is precisely why I tend to recommend using SIP for mobile network programming. SIP is the standard connection protocol for mobile carriers. In addition, libraries for it are easy to find and use. For an introduction to SIP, see my introductory articles: An Introduction to SIP, Part 1: Meet SIP and An Introduction to SIP, Part 2: SIP Servlets. It's very straightforward to program for SIP using Java ME. The latest mobile libraries constitute a rich programming environment that makes application development a breeze. This tutorial shows how to build a simple messenger client for your mobile phone. It uses the SIP protocol and is built using Java ME libraries. The application can be run on its own, or it can be configured to use a SIP registrar, such as BEA WebLogic SIP Server. PrerequisitesTo get the most out of this tutorial, you'll need to install the following on your development box:
Moreover, you'll need to know a bit of Java ME. For assistance with these prerequisites, read the Appendix. The MEssenger ApplicationI decided to demonstrate the use of SIP in Java ME by developing an instant messaging client application. This application is extremely simple, but it demonstrates sending messages (REGISTER, MESSAGE), processing responses, and handling incoming messages. I decided to name this application MEssenger (pronounced "mee-senger," and the "ME" stands for Micro Edition, as in Java ME). It will have a very simple GUI with two pages. The main page is for sending and receiving messages. The second page is for configuring the application. Anything fancier would be out of the scope for this article. The navigation for MEssenger is shown in Figure 1. Sending a message is performed by entering the destination SIP address and the message, and then choosing Send in the menu. The last five events of the application are displayed in the second half of the screen. The configuration page allows entering of registration information.
Before diving into the code behind this application, let's look at its design. DesignThe application I'm going to write is composed of the following five classes and interfaces:
As I said earlier, it's not the objective of this article to introduce you to Java ME, so I will concentrate on the class About SIP API for Java MESIP programming with Java ME feels a bit like socket programming. You'll see concepts like opening and closing client and server connections, streaming, and threads. I'll show you the various classes that are needed to do all this using examples. But first I'll list a few key classes of this API:
There are three typical "recipes" that you can use with these classes. We'll see each of these in turn:
Before I show you these recipes, it's necessary to do some groundwork. Let's look at how to create a Creating a
|
| Parameter | Description |
|---|---|
| Register | Boolean, contains true if the SIP client registers itself to a registrar. |
| Username | String, the identifier to use in the SIP address of the client. For example this would correspond to the username section in sip:username@10.0.0.3:5060. |
| Port | Int, the port to use by the SIP client. Typically this is 5060, but if you're running multiple SIP clients and a server on the same machine, you want to use a different address. |
| Registrar | String, address of the registrar, including the port. For example: 10.0.0.3:5060 in case the SIP address is
sip:username@10.0.0.3:5060. |
| Expires | Int, number of seconds a registration lasts. |
Of course, there are getters and setters for all these parameters.
The SipManager also contains other private members, like the SipConnectionNotifier object which receives messages, addresses to use, and the identifier for recurring requests. I'll discuss these later on.
The simplest operation that can be performed using SIP is sending a single message. Figure 2 illustrates this operation:

Figure 2. Sending a request
As you can see, the process of sending a message is twofold. First there's the preparing and sending of the message. Second, there's the processing of the response. Let's have a look at the code to do this. I put the first step in the method
SipManager.sendMessage():
public void sendMessage(final String destination, final String message) {
Thread t = new Thread() {
public void run() {
SipClientConnection connection = null;
OutputStream output = null;
try {
connection = (SipClientConnection) Connector
.open(destination);
connection.setListener(SipManager.this);
connection.initRequest("MESSAGE", null);
connection.setHeader("From", registeredAddress);
connection.setHeader("To", destination);
connection.setHeader("Content-Type", "text/plain");
connection.setHeader("Content-Length", String
.valueOf(message.length()));
output = connection.openContentOutputStream();
output.write(message.getBytes());
output.close();
output = null;
} catch (Throwable e) {
messageListener.notifyMessage("Error sending to "
+ destination + ": " + e.getMessage());
e.printStackTrace();
try {
if (output != null) {
output.close();
}
if (connection != null) {
connection.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
};
t.start();
}
You'll notice that this method starts a new thread. You'll see this pattern in a few places in the example application. Why is that? Because communication may take time, and you don't want your GUI to be unresponsive during this time. Moreover, a deadlock could occur when the GUI thread is waiting for the communication to finish, and the communication triggers a GUI change.
This sample code is relatively simple. I open a client connection, I use this as the listener of responses, I initialize the request type, and I set a bunch of mandatory headers. Most of the needed SIP headers for the request will be automatically populated with default values. Then I open an output stream and write out the message to this stream. At the end I close the stream. I'm not closing the connection just yet; I'm just waiting for a response to arrive.
One thing that's worth knowing is that the content is optional. A request can be empty. In this case, the method that sends out the message is SipClientConnection.send() instead of just closing the stream. Other methods are available for customizing a request. Here are a few:
initCancel(): creates a CANCEL request. Use instead of initRequest().initAck(): creates an ACK request. Use instead of initRequest().setRequestUri(): changes the default request URI value.addHeader(): used to insert headers that can be repeated—Contact, for example.setCredentials(): used to add authentication headers.For a response to be processed, my SipManager must implement SipClientConnectionListener. This contains a single method, namely notifyResponse(). It is invoked automatically when a response arrives. My first shot at an implementation checks for the kind of request related to this response, and then displays a message:
Finally, it closes the connection.
public void notifyResponse(SipClientConnection connection) {
try {
connection.receive(0);
String method = connection.getMethod();
if (method.equals("MESSAGE")) {
int status = connection.getStatusCode();
if (status == 200) {
messageListener.notifyMessage("Sent OK");
} else {
messageListener.notifyMessage("Error sending: " + status
+ " " + connection.getReasonPhrase());
}
return;
}
/* Registration code goes here. */
} catch (Throwable e) {
messageListener.notifyMessage("Error sending: " + e.getMessage());
} finally {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
That's all there is to this recipe. It was really simple. Let's move on to the next one.
There are two ways to process an incoming request. The first way, the synchronous approach, involves blocking the current thread to wait for one to arrive. I don't think this is very elegant, but it works fine if you know that you're going to receive a request at or around a certain time. I'm not going to cover this technique as I think it's of limited use.
The second way is to open a "permanent" server connection and get notified when a message arrives asynchronously. This is the preferred technique, and it's the one I'm going to use here.
Figure 3 shows how requests can be processed by my application:

Figure 3. Receiving a request
As with sending a request, there are two steps for receiving one. The first step is to register a listener of incoming messages against a server connection. The second step involves being notified of the incoming request and sending out a response. This code snippet covers the first step:
public void reconnect() {
Thread t = new Thread() {
public void run() {
doClose();
try {
sipConnection = (SipConnectionNotifier) Connector
.open("sip:" + port);
} catch (Throwable e) {
e.printStackTrace();
}
try {
sipConnection.setListener(SipManager.this);
contactAddress = "sip:" + username + "@"
+ sipConnection.getLocalAddress() + ":"
+ sipConnection.getLocalPort();
} catch (Throwable e) {
e.printStackTrace();
}
/* Registration code goes here. */
};
t.start();
}
Note how the parameter to Connector.open() is using the syntax:
sip:port
Isn't it supposed to be sip:username@registraraddress:port? Using a real SIP address would create a client connection. Using just sip: or sips: followed by the port number creates a server connection. (There are other ways to create a server connection, but it's not necessary to know about those to understand how MEssenger works. For more information, refer to the Javadoc page of
SipConnection.)
Pages: 1, 2 |
Article Tools
Related Products
Check out the products mentioned in this article:Bookmark Article
| del.icio.us | |
| Digg | |
| DZone | |
| Furl | |