Introduction to the SIP API for Java ME

by Emmanuel Proulx
05/22/2007

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 Go

Mobile 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:

  • It's easier to develop when libraries exist.
  • It provides more control, for example, billing based on the type of interaction, not only on the number of kilobytes downloaded.
  • Mobile carriers may block non-standard protocols.
  • It's more interoperable with a variety of devices.

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.

Prerequisites

To 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 Application

I 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.

MEssenger navigation
Figure 1. MEssenger navigation

Before diving into the code behind this application, let's look at its design.

Design

The application I'm going to write is composed of the following five classes and interfaces:

Class Description
MEssengerMIDlet The "Main" class for this Java ME application. It creates an instance of MenuManager and displays it.
MenuManager This class contains pages and navigation events. It also instantiates the SipManager class. This class implements the MessageListener interface.
SipManager This contains the communication behavior; sending and receiving of messages, scheduling of registrations.
ErrorAlert Utility class to display an error.
MessageListener Interface used by the SipManager to request that the MenuManager display messages. This decouples the two classes, and you can reuse the SipManager in other applications that don't use the MenuManager.

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 SipManager for the rest of this article. For more information on other classes, please refer to the included source code.

About SIP API for Java ME

SIP 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:

Class Description
Connector The factory to create all kinds of connection objects. For SIP connections, simply use an address that begins with "sip:" and the Connector creates either SipClientConnection or SipServerConnection objects.
SipClientConnection This class is used to send out SIP messages that are not recurrent, like INVITE and MESSAGE.
SipClientConnectionListener This interface must be implemented by classes that wish to process SIP responses.
SipServerConnectionListener This interface must be implemented by classes that plan to receive SIP requests.
SipServerConnection This class reads incoming messages.
SipRefreshHelper This is a utility class to manage recurring outgoing SIP messages, like REGISTER and SUBSCRIBE.
SipRefreshListener Implement this interface to process responses for recurring messages.

There are three typical "recipes" that you can use with these classes. We'll see each of these in turn:

  • Send a single request.
  • Receive a request.
  • Send a recurring request.

Before I show you these recipes, it's necessary to do some groundwork. Let's look at how to create a SipManager.

Creating a SipManager

Although you don't have to design your application this way, I decided to encapsulate the entire SIP messaging in a single class, namely the SipManager. As stated earlier, this is a reusable class that doesn't make assumptions about its execution environment.

Create a new class in your existing MIDlet project. Call it SipManager. Now I'll go to the Java editor and start coding. SipManager implements the following interfaces:

                          public class SipManager implements SipServerConnectionListener,      SipRefreshListener, SipClientConnectionListener {
                      

The single constructor saves the caller's reference in case there are messages to display down the road. It also initiates a registration (if this feature is turned on) and starts to listen for incoming messages. I call this "connecting." I will discuss connections later on.

                          public SipManager(MessageListener messageListener) throws IOException {    this.messageListener = messageListener;    reconnect();  }
                      

SipManager contains a number of fields. I'm skipping this trivial code for brevity. Some of these fields are parameters that can be modified in the MEssenger configuration page:

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.

Sending a Request

The simplest operation that can be performed using SIP is sending a single message. Figure 2 illustrates this operation:

Sending a request
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:

  • OK, if the message was sent successfully.
  • Error, if there was a problem sending the 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.

Receiving a Request

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:

Receiving a request
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

Next Page »