Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Getting Started with SIP API for J2ME (JSR 180)

 
By Qusay H. Mahmoud, November 2004  

The Session Initiation Protocol (SIP) is a signaling protocol used for establishing and controlling multimedia communication sessions on networks that use the Internet Protocol (IP). A session may be as simple as a two-way phone call or as complex as a collaborative video conference. SIP was primarily designed to establish, modify, and terminate sessions, and therefore doesn't know about the details within a session. This simplicity makes SIP quite scalable and extensible, and it can be used easily in different architectures and deployment scenarios. SIP is already being used for all kinds of innovative applications and services, and its importance in mobile networks will continue to grow. In addition, now that MIDP 2.0 has included support for TCP/IP sockets and UDP/IP datagrams, SIP will become an important protocol in IP-based mobile phone environments.

This expectation that SIP will play a rapidly growing role in the mobilility space led to the development of the SIP API for J2ME (JSR 180) within the Java Community Process (JCP). The motive behind the initiative was to define a single standard SIP interface with strong security early, before competing standards, with less robust security models, could gain wide acceptance.

After an overview of SIP, this article:

  • Presents a tutorial that introduces SIP API for J2ME
  • Describes how to download, install, and run the JSR 180 reference implementation, available from Nokia
  • Discusses how to develop MIDlets that use SIP API for J2ME
  • Demonstrates how to compile SIP MIDlets using the J2ME Wireless Toolkit
  • Offers a taste of the effort involved in developing SIP MIDlets
Overview of Session Initiation Protocol

The Session Initiation Protocol (SIP) was defined by the Internet Engineering Task Force (IETF), in collaboration with the Third Generation Partnership Project (3GPP), as RFC 2543, later superseded by RFC 3261. It's a request-response protocol, just like HTTP, so using it makes telephony just another web application, which can be integrated into Internet applications and services easily.

3GPP has specified SIP to be the network protocol of choice for the all-IP packet-switched third-generation network. The IP Multimedia Subsystem (IMS) represents the architecture defined in 3GPP Releases 5 and 6 for SIP-based 3G networks. Within the IMS network, mobile terminals establish voice calls using the SIP protocol. SIP is used for managing sessions between two or more IP endpoints. It defines user agents that initiate and respond to SIP requests, and proxies that route SIP messages to their destinations. This scheme leads to a host of innovative services: voice-based e-commerce, web-page click-to-dial, location services, and many more. In addition, it can be used to provide instant messaging, presence, and gaming services.

JSR 180: SIP API for J2ME

JSR 180 defines an optional package that provides a general SIP API for J2ME clients, so that SIP applications can operate in devices with limited memory. This interface enables Java-enabled mobile devices to send and receive SIP messages. The API is compact, so it has a small footprint, and generic, so you can use it with any J2ME profile, not just MIDP. As a minimum, it requires version 1.0 of the Connected Limited Device Configuration (CLDC), but the APIs can be used with the Connected Device Configuration (CDC) as well. It provides SIP functionality at the transaction level; it's integrated into CLDC's Generic Connection Framework (GCF).

The APIs are defined in the package javax.microedition.sip. This package contains eight interfaces and four classes:

Interfaces Description
SipConnection The base interface for SIP connections; holds the common properties and methods for subinterfaces SipClientConnection and SipServerConnection.
SipClientConnection Represents a SIP client transaction. An application can create a new SipClientConnection using javax.microedition.io.Connector or SipDialog.
SipServerConnection Represents a SIP server transaction; created by the SipConnectionNotifier when a new request is received.
SipConnectionNotifier Defines a SIP server connection notifier, which queues incoming messages. To receive incoming requests, applications use this interface's acceptAndOpen() method, which accepts and opens a new SipServerConnection. If there are no messages in the queue, the method blocks until a new request is received.
SipServerConnectionListener A listener interface for incoming SIP requests.
SipClientConnectionListener A listener interface for incoming SIP responses.
SipDialog Represents one SIP dialog. The SipDialog can be retrieved from a SipConnection object when it's available, which happens after an INVITE or SUBSCRIBE request.
SipRefreshListener A listener interface for the RefreshHelper class; declares a refreshEvent() method that takes a refreshID to identify the corresponding refresh task, and a statusCOde representing the result of the refresh: 0 for cancellation, 200 for success, and anything else for failure.


Classes Description
SipAddress Provides a generic SIP address parser. It can be used to parse a SIP address, or to construct one.
SipHeader Provides a generic SIP header parser helper. It can be used to parse string header values read from SIP messages, or to construct SIP headers.
SipRefreshHelper Implements the functionality that facilitates the handling of refreshing requests on behalf of the application.
SipException An exception class for SIP-specific errors.


SIP API for J2ME is meant to be used by applications that need to implement SIP user-agent functionality. They can use SipClientConnection to implement a SIP user-agent client, and use SipConnectionNotifier and SipServerConnection to implement a SIP user-agent server. A mobile device may act as a SIP client sending requests to SIP servers, and also as a SIP server accepting requests from SIP clients, as Figure 1 illustrates:

Figure 1: Mobile Devices Acting as Both Clients and Servers
Figure 1: Mobile Devices Acting as Both Clients and Servers


The SIP APIs are integrated into CLDC's Generic Connection Framework. For example, an application instantiates a new SIP connection by calling Connector.open(), which will return either a SipClientConnection or a SipServerConnection, depending on the SIP URI passed to open(). Your application should call the Connector.close() method when it has finished with the connection.

The SIP URI has the following syntax...

{scheme}:[{target}][{params}]

...where:

  • scheme is the SIP scheme supported by the system, sip or sips. In the sips protocol, the server connection accepts only requests over secure transport, as defined in the RFC 3261 standard.
  • target is the user address, which can be something like userName@target-host.com:portNumber or just a phone number.
  • params represents additional parameters; for example, transport=udp to specify UDP as the transport protocol.

If the target host is included, a new client connection is opened; if it's not, a new server connection is opened. Here are some examples:

...
// client connections
SipClientConnection scc = (SipClientConnection)
    Connector.open("sip:max@company.com");
SipClientConnection scc2 = (SipClientConnection)
    Connector.open("sip:jack@128.11.1.2:5100");
...

...
// server connection
SipConnectionNotifier scn = (SipConnectionNotifier)
    Connector.open("sip:5500");
...

...
// server connection where the port number is allocated by the system
SipConnectionNotifier scn2 = (SipConnectionNotifier)
    Connector.open("sip:");
...


The following method demonstrates how to open a SIP client connection to a server that's running on host 102.12.1.1 and listening on port 5050.

private void sendMessage(String message) {
    SipClientConnection sc = null;
    try {
        // open SIP connection to server 102.12.1.1 on port 5050
        sc = (SipClientConnection)
            Connector.open(sip:user@102.12.1.1:5050);
        // initialize SIp request MESSAGE
        sc.initRequest("MESSAGE", null);
        // set the message header
        sc.setHeader("Subject", subject.getString());
        sc.setHeader("Content-Type", "text/plain");
        sc.setHeader("Content-Length", ""+text.length());
        // send the message to the network
        OutputStream os = sc.openContentOutputStream();
        os.write(message.getBytes());
        os.close(); // close the connection
    } catch(Exception ex) {
        ex.printStackTrace();
    }
}


The next code segment implements the notifyRequest() method of the SipServerConnectionListener interface. When the server receives the initialization SIP request MESSAGE, it retrieves the headers of the message and its content, then sends a response back to the client.

public void notifyRequest(SipConnectionNotifier scn) {
   try {
      ssc = scn.acceptAndOpen();
      if(ssc.getMethod().equals("MESSAGE")) {
         String contentType = ssc.getHeader("Content-Type");
         String contentLength = ssc.getHeader("Content-Length");
         int length = Integer.parseInt(contentLength);
         if((contentType != null) && contentType.equals("text/plain")) {
            InputStream is = ssc.openContentInputStream();
            int i=0;
            byte testBuffer[] = new byte[length];
            i = is.read(testBuffer);
            String tmp = new String(testBuffer, 0, i); 
               // the message received
         }
         ssc.initResponse(200); // OK
         ssc.send();
      }    
   } catch(IOException ex) {
      // ex.getMessage();
   }
}


The JSR 180 Reference Implementation

A reference implementation for JSR 180 is available from Nokia. It's a pure Java implementation that's built on CLDC 1.1 and MIDP 2.0. It supports real SIP messaging over a local area network, enabling applications to send SIP messages, receive SIP messages synchronously, and receive SIP message-listener notifications asynchronously.

To download the JSR 180 RI, you must be a member of Forum Nokia; registration is free. Once you've become a member, download and unpack the archive. You'll get a directory structure like this:

/sip/sip1_0-ri-bin
    /examples (sample applications)
    /lib (JAR file containing the SIP APIs)
    /midp (executable MIDP 2.0 and CLDC 1.1 that includes the SIP APIs)


The midp directory contains several subdirectories, one of which is bin. This directory contains several tools, including sipa-midp.exe, which launches the SIP-enabled emulator. You can use its -help argument to list all the options.

To start experimenting with the examples that come with the JSR 180 RI, run sipa-midp. You'll see four sample MIDlets, as in Figure 2.

Figure 2: Sample SIP MIDlets That Accompany the JSR 180 RI
Figure 2: Sample SIP MIDlets That Accompany the JSR 180 RI


You can test SendMessage and ReceiveMessage together. Start these two MIDlets, each in its own emulator window. The emulators can run on the same host or on different hosts; just make sure you know the IP address or hostname where ReceiveMessage is running. Figures 3A and 3b show the sample in action.

Figure 3a: SendMessage
Figure 3a: SendMessage
Figure 3b: ReceiveMessage
Figure 3b: ReceiveMessage


To experiment with the OriginatingINVITE and TerminatingINVITE MIDlets, open them in different emulators – again, they can be on the same or different hosts. Once the MIDlets are running, fill out the details and you'll see something like Figures 4a and 4b. Here the OriginatingINVITE has sent INVITE and ACK, and the TerminatingINVITE has answered with 180 Ringing and 200 OK:

Figure 4a: OriginatingINVITE
Figure 4a: OriginatingINVITE
Figure 4b: TerminatingINVITE
Figure 4b: TerminatingINVITE


Programming SIP MIDlets

A good way to learn how to develop SIP MIDlets is by adapting existing MIDlets. SipMIDlet, which comes with the reference implementation, shows how to open a connection to a SIP server, send messages, and receive replies:

import java.util.*;
import java.io.*;
import javax.microedition.io.*;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

import javax.microedition.sip.*;

public class SipMIDlet extends MIDlet
    implements CommandListener, SipServerConnectionListener  {

    private Display display;
    private long startTime;
    private Form form;
    private TextField receivePort;
    private Command receiveCmd;
    private Command exitCmd;
    SipConnectionNotifier scn = null;
    SipServerConnection ssc = null;

    public SipMIDlet() {
        System.out.println("MIDlet: SipMIDlet starting...");
        display=Display.getDisplay(this);
        form = new Form("Receive Message");
        receivePort = new TextField
            ("Give receive port:", "sip:5060", 30,
            TextField.LAYOUT_LEFT);
        form.append(receivePort);
        receiveCmd = new Command("Start", Command.ITEM, 1);
        exitCmd = new Command("Exit", Command.EXIT, 1);
        form.addCommand(receiveCmd);
        form.addCommand(exitCmd);
        form.setCommandListener(this);
    }
    
    public void commandAction(Command c, Displayable d) {
        if(c == receiveCmd) {
            Thread t = new Thread() {
                public void run() {
                receiveMessage();
                }
            };
            t.start();
        }
        if(c == exitCmd) {
            if(scn != null) {
                try {
                    scn.close();
                }
                catch(IOException iox) {
                }
            }
            destroyApp(true);
        }
    } 
    
    public void startApp() {
        display.setCurrent(form);    
        System.out.println("MIDlet: SipMIDlet startApp()");
    }
    
    public void receiveMessage() {
        try {
            if(scn != null)
            scn.close();
            scn = (SipConnectionNotifier)
                Connector.open(receivePort.getString());
            scn.setListener(this);
            form.append("Listening... in port: "+scn.getLocalPort());
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }
    
    public void notifyRequest(SipConnectionNotifier scn) {
        try {
            ssc = scn.acceptAndOpen();
            if(ssc.getMethod().equals("MESSAGE")) {
            String contentType = ssc.getHeader("Content-Type");
            String contentLength = ssc.getHeader("Content-Length");
            int length = Integer.parseInt(contentLength);
            if((contentType != null) &&
                contentType.equals("text/plain")) {
                InputStream is = ssc.openContentInputStream();
                int i=0;
                byte testBuffer[] = new byte[length];
                i = is.read(testBuffer);

                String tmp = new String(testBuffer, 0, i);

                StringItem st = new StringItem
                    ("Subject:", ssc.getHeader("Subject"));
                form.append(st);
                st = new StringItem("Message:", tmp);
                form.append(st);
            }
            ssc.initResponse(200);
            ssc.send();
        }
        
        } catch(IOException ex) {
            form.append("Exception: "+ex.getMessage());
        }
    
    }
    
    public void pauseApp() {
        System.out.println("MIDlet: pauseApp()");
    }

    public void destroyApp(boolean b) {
        System.out.println("MIDlet: destroyApp()");
        notifyDestroyed();
    }   
}


Using the J2ME Wireless Toolkit to Develop SIP MIDlets

The easiest way to build SIP MIDlets is to use the J2ME Wireless Toolkit. Note that even the latest release of the toolkit, version 2.2, doesn't support SIP APIs for J2ME and therefore can't actually run MIDlets that use them. You can, however, configure the Wireless Toolkit so that it can build MIDlets that use the SIP APIs for J2ME, then test the MIDlets in the Nokia emulator that comes with the JSR 180 RI. This section shows you how.

Once you have J2ME Wireless Toolkit 2.2 installed, the key task is to copy the SIP APIs that come with the Nokia reference implementation into the toolkit's midp20api.jar file. Use your favorite archiving tool; for example, many Windows users would use WinZip, thus:

  1. In WinZip, open /<pathToSIPROOT>/lib/sipa1_0.jar.
  2. In a second WinZip window, open /<wtk22>/lib/midp20.jar.
  3. Drag and drop the classes from sipa1_0.jar to midpapi20.jar.
  4. Close both archives.

Now run the J2ME Wireless Toolkit. Create a project to build SipMIDlet, and call it whatever you like – MySIP in my example. Because you cannot run the SIP MIDlet in the toolkit's emulator, and the RI's emulator supports only CLDC 1.1 and MIDP 2.0, you should configure the project settings as in this dialog:

Figure 5: J2ME Wireless Toolkit Project Configuration
Figure 5: J2ME Wireless Toolkit Project Configuration


Now, write a SIP MIDlet, or simply copy the source of the ReceiveMessage example you saw earlier and rename it SipMIDlet. Save it in /<wtk22>/apps/MySIP/src, and build the project in the toolkit.

If you try to run the MIDlet in the J2ME Wireless Toolkit, an exception will be thrown, and it's not quite ready for testing in the Nokia emulator that comes with the JSR 180 RI. You first need to package the MIDlet. In the toolkit's menu, simply choose Project -> Package -> Create Package, which will create MySIP.jar in /<wtk22>/apps/MySIP/bin. Now you can run the SipMIDlet in the Nokia MIDP emulator with the following command:

/<pathToSIPROOT>/bin/midp/bin/sipa-midp -classpath /<wtk22>/apps/MySIP/bin/MySIP.jar SipMIDlet


Other SIP-related JSRs

Within the JCP, a number of JSRs have resulted in standard SIP APIs: JAIN SIP API Specification (JSR 32) specifies a standard interface to share information between SIP clients and SIP servers. SIP Servlet API (JSR 116) defines a high-level extension interface to enable SIP servers to deploy and manage SIP applications based on the servlet model. Finally, JSR 180 defines a general SIP API for J2ME clients, to make it possible for SIP applications to run in devices with limited memory.

For more information on Java support for SIP, please see the white paper SIP and the Java Platforms.

Conclusion

The SIP protocol was designed with the Internet in mind. With its request-response model, it resembles HTTP and other Internet protocols. As a result, telephony simply becomes another web application that can be integrated into other applications and services easily. Compared to legacy mobile-network protocols, SIP is easy to program to, so it will become an important tool for establishing communication sessions. It can be used to implement all kinds of peer-to-peer applications that feature voice, video, messaging, and gaming.

This article presented an overview of SIP, and a tutorial on SIP API for J2ME. The sample code showed how little effort is required to develop SIP applications; there's only a handful of classes and interfaces you need to learn about to get started. Following the instructions in this article, you should find it easy to configure your J2ME Wireless Toolkit to serve as your development environment for SIP MIDlets.

For more information
Acknowledgments

Special thanks to Kenneth K. Lui, Phelim O'Doherty, and Steven Grover of Sun Microsystems, for their contributions to this article.

About the author

Qusay H. Mahmoud provides Java technology consulting and training services. He has published dozens of Java articles, and is the author of Distributed Programming with Java (Manning Publications, 1999) and Learning Wireless Java (O'Reilly, 2002).

Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
Your email address (no reply is possible without an address):
Sun Privacy Policy

Note: We are not able to respond to all submitted comments.