Introduction to the SIP API for Java ME
Pages: 1, 2

The interface SipConnectionNotifier is quite interesting. I use it here to register a listener of incoming requests. But I also use it to retrieve the device's address. It doesn't sound like much, but you should know that this is not available any other way as far as I know. (Why this isn't available through a non-SIP API is beyond me.) It can also be used to block and wait for an incoming request via its acceptAndOpen() method.

Once this server connection is open, it will notify the SipManager automatically of an incoming request message. I will then read in the message, and send out the appropriate response using a SipServerConnection object. Here's how:

                          public void notifyRequest(SipConnectionNotifier notifier) {   SipServerConnection connection = null;   InputStream input = null;   try {     connection = notifier.acceptAndOpen(); //Shouldn't block     String size = connection.getHeader("Content-Length");     int length = Integer.parseInt(size);      if (length == 0) {       connection.initResponse(200);       connection.send();       return; //nothing else to do...     }      byte buffer[] = new byte[length];     int readSize;     input = connection.openContentInputStream();     readSize = input.read(buffer);      String from = connection.getHeader("From");     SipAddress sipAddress = new SipAddress(from);     from = sipAddress.getDisplayName();     if (from != null)       from = from.trim();     if ((from == null) || (from.equals("")))       from = sipAddress.getURI();      String message = "From " + from + ": ";     message += new String(buffer, 0, readSize);      messageListener.notifyMessage(message);      //All done, reply OK.     connection.initResponse(200);     connection.send();   } catch (Throwable e) {     e.printStackTrace();   } finally {     try {       if (input != null)         input.close();       if (connection != null)         connection.close();     } catch (Throwable e) {       e.printStackTrace();     }   } }
                      

The parameter SipConnectionNotifier is a factory to a SipServerConnection object. Notice how SipServerConnection is used both to receive the request and to send back the response. Methods of SipServerConnection are very similar to SipClientConnection, including methods to get and set headers and contents—except of course the init...() methods which are replaced by initResponse(int statusCode). Moreover, you can use setReasonPhrase(String reason) to replace the default text that appears next to the status code in the response.

Note that closing the SipServerConnection does not close the SipConnectionNotifier that created it. It simply means that the current operation is finished.

Let's now move on to our final recipe.

Sending a Recurring Request

Some requests like REGISTER and SUBSCRIBE are meant to be sent over and over again at regular intervals. This job is made easier by using the refresh mechanism included in the SIP API for Java ME.

The steps involved in recurring requests are shown in Figure 4 and 5. Figure 4 looks a lot like the sending a request recipe, but there is one difference. Can you spot it?

First registration
Figure 4. First registration

The one difference is the invocation of the method SipClientConnection.enableRefresh(). This method is used to turn on automatic refreshing of requests and to specify a listener for refresh events. The identifier returned can later be used to stop the refresh task. I'll discuss this in a bit. The response to the first REGISTER message is sent to the notifyResponse() method.

Subsequent registration
Figure 5. Subsequent registrations

The SipRefreshHelper uses some kind of timer to schedule updates to the request, just before the request expires. Responses to subsequent requests are sent to a RefreshListener provided earlier.

Let's go through the code that goes with the figures. Earlier, I left a couple of comments in the code, like this one:

/* Registration code goes here. */

This marks the places where registration code snippets must be inserted. The first snippet sends the first REGISTER message from inside the reconnect() method. I put it in bold below:

                          public void reconnect() {   Thread t = new Thread() {     public void run() {  // First half hidden for brevity                                     registeredAddress = "sip:" + username + "@" + registrar;        if (!register)         return;       try {         SipClientConnection registerConnection=createRegisterConnection();         registerConnection.setListener(SipManager.this);         refreshIdentifier = registerConnection             .enableRefresh(SipManager.this);         registerConnection.send();         registerConnection.close();       } catch (Throwable e) {         e.printStackTrace();       }     }   };   t.start(); }                         
                      

I think the code speaks for itself. Note the use of the enableReferesh() method. The next snippet is invoked as a response to the first REGISTER message as part of the method notifyResponse():

                          public void notifyResponse(SipClientConnection connection) {   try {  // First half hidden for brevity                                   if (method.equals("REGISTER")) {       int status = connection.getStatusCode();       if (status == 200) {         messageListener.notifyMessage("Registration OK");       } else {         messageListener.notifyMessage("Error registering: "             + status + " " + connection.getReasonPhrase());       }       return;     }   } catch (Throwable e) {     messageListener.notifyMessage("Error sending: " + e.getMessage());   } finally {     try {       connection.close();     } catch (IOException e) {       e.printStackTrace();     }   } }                         
                      

The final snippet of the registration code implements the RefreshListener interface. It consists of a single method, refreshEvent():

                          public void refreshEvent(int refreshID, int statusCode, String reasonPhrase) {   if (statusCode == 200) { //OK     messageListener.notifyMessage("Re-registered OK.");   }   else { //ERROR!     messageListener.notifyMessage("Error registering: " + statusCode);     SipRefreshHelper.getInstance().stop(refreshIdentifier);   } }
                      

This simply displays a message regarding the state of the registration, and, for error conditions, it stops the refresh timer.

Cleanup Code

My example is almost complete. The missing piece is code that cleans up by closing connections and stopping refresh timers. This is invoked when the application closes.

                          public void close() {   Thread t = new Thread() {     public void run() {       doClose();     }   };   t.start(); }  protected void doClose() {   if (contactAddress == null)     return; //No need to unregister and close connection; there wasn't a connection.    try {     if (register)       SipRefreshHelper.getInstance().stop(refreshIdentifier);   } catch (Throwable e) {     e.printStackTrace();   }   try {     if (sipConnection != null) {       sipConnection.close();       sipConnection = null;     }   } catch (Throwable e) {     e.printStackTrace();   } }
                      

First I stop the refresh task. This also sends an unregister message (a REGISTER message with the Expires header equal to 0). Then I close the server connection. I do all this in a new thread so that I don't interfere with the GUI thread.

The Result

My small but so useful MEssenger is all done. To see it in action, you could look at Figure 6 below:

MEssenger in action
Figure 6. MEssenger in action

Alternatively, you can obviously run the application directly on your mobile phone!

Using a Registrar

If you want to use registration, you're going to need a registrar. There's a registrar and proxy provided with BEA WebLogic SIP Server. This section will describe how to prepare and use them.

At the time this article was written, the proxy didn't handle SIP MESSAGE messages. I'll have to configure the proxy so that it will handle them. This is done simply by editing the file C:\bea\sipserver30\samples\sipserver\examples\src\registrar\WEB-INF\sip.xml (this is the default installation folder; use whichever folder you installed to) and by adding the following tags:

                                                      <servlet-mapping>     <servlet-name>proxy</servlet-name>     <pattern>       <equal>         <var>request.method</var>         <value>MESSAGE</value>       </equal>     </pattern>   </servlet-mapping>                         
                      

Once this is done, build and deploy the registrar application using these steps:

  1. Create an environment variable, WL_HOME, pointing to the SIP server folder. This can be done, for example, in a command window by typing:
    set WL_HOME=c:\bea\sipserver30
    (This is the default installation folder, use the folder that you used when you installed the SIP Server.)
  2. Add weblogic.jar to your classpath. This can be done, for example, in a command window by typing:
    set classpath=%CLASSPATH%;%WL_HOME%\server\lib\weblogic.jar
  3. Now it's time to build. Go to the registrar source folder:
    cd %WL_HOME%\samples\sipserver\examples\src\registrar
  4. Next, use Ant to build:
    ant build
  5. Create a WebLogic SIP Server domain to run the application in.
  6. Finally, deploy the application to the running server:
    ant deploy.

You can now let MEssenger register to the SIP server.

MEssenger is also compatible with the ChatRoomServer servlet I spoke about in a previous article.

Summary

This tutorial illustrates how easy it is to program with SIP using Java ME. With the simple MEssenger application, implementations of a number of useful communication patterns were demonstrated too. The simplicity of the library combined with the flexibility of SIP allows countless applications to be developed.

My friends' mobile phones and mine are now IM-enabled, and we use them regularly to chat with each other. Have fun with your mobile phone!

References

  • JSR 180
  • Registrar example documentation, located by default at C:\bea\sipserver30\samples\sipserver\examples\src\registrar\readme.html

Appendix

This note provides tips on how to install and configure the Java Wireless Toolkit and EclipseME. The objective is to get you ready to develop SIP applications on the Java ME platform.

Installation tips for the Java Wireless Toolkit

Java Wireless Toolkit (WTK) is a bunch of tools that help in developing applications for mobile phones and similar devices. It contains tons of libraries (including an implementation of SIP API for Java ME) and an emulator, all packaged together in an easy-to-install bundle. This makes it an easy choice for a SIP development environment.

The newest Java Wireless Toolkit is available on the Java ME downloads page, here.

Navigate to the download page. You'll need to register (for free) before you can download the installer. You must log in using your Download Center username and password at this point.

The Windows installer is named something like j2me_wireless_toolkit-x_y-windows.exe (where x and y are the major and minor version numbers). Download the file to your hard drive. Run the executable and follow the steps on the screen. Quick Time Player is optional and lets you play media files in the emulator. If you don't have the Quick Time Player and you wish to install it, go to this address.

http://www.apple.com/quicktime/download/standalone.html

Installation tips for EclipseME

Eclipse ME is a plug-in for Eclipse that allows easy development of Java ME applications. It's fully integrated with the Java Wireless Toolkit. You set up Eclipse ME in two stages. First you install EclipseME. Second you configure it. This section covers the first stage.

The easiest way to get EclipseME is to use the Eclipse Software Updates. Start Eclipse and go to the menu Help > Software Updates > Find and Install. Select "Search for new features to install," and click Next. On the next page click the button New Remote Site. Enter the following information:

  • Name: EclipseME
  • URL: http://www.eclipseme.org/updates

Click Finish. Eclipse will now look for EclipseME at the update site. In the next dialog select EclipseME and keep going until you're done with the installation.

Click Install to continue. You'll have to restart Eclipse after EclipseME is installed.

Configuration tips for EclipseME

Earlier I said that EclipseME is integrated with Java Wireless Toolkit. This may be true, but you still have to do some configuring to make them work together. Here are the steps you need to take:

  1. J2ME Preferences: In Eclipse, go to the Preferences dialog (menu Window > Preferences). Navigate to the category J2ME. You need to input the location of the Java Wireless Toolkit in the WTK Root field.
  2. Navigate to the Device Management category. The list of devices is empty. EclipseME can search for devices for you. Click Import. Enter the WTK folder again, and select all devices. Click Finish. The devices are now imported in the list. Select one device to use as the default.
  3. Debug Settings: Certain settings are needed to make the Java Wireless Toolkit work in the debugger. You must set the following options in the Java > Debug category:
    • Suspend execution on uncaught exceptions: NOT selected.
    • Suspend execution on compilation errors: NOT selected.
    • Debugger timeout (ms): 15000 (which is 15 seconds).

Without these options, debugging will not work.

Configuration Tips for EclipseME

Installation is now over. Let's test things out to make sure everything works.

  1. Create a J2ME Project: In Eclipse go to menu File > New > Project. Select the category J2ME > J2ME Midlet Suite. Click Next. Enter a project name. Click Next. Choose the name for the deployment file (JAD file). Click Finish.
  2. Create a package.
  3. Create a MIDlet: Go to menu File > New > Other. Navigate to J2ME > J2ME Midlet. Click Next. Enter the name of your class. Click Finish.
  4. When you're done, look at the generated MIDlet code.

Learning about Java ME programming

Many online tutorials are available. Here are a few:

Emmanuel Proulx is an expert in J2EE and SIP. He is a certified WebLogic Server engineer.