Articles
Enterprise Architecture
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.
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?
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.
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.
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.
My small but so useful MEssenger is all done. To see it in action, you could look at Figure 6 below:
Figure 6. MEssenger in action
Alternatively, you can obviously run the application directly on your mobile phone!
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:
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 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.jarcd %WL_HOME%\samples\sipserver\examples\src\registrarant buildant 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.
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!
C:\bea\sipserver30\samples\sipserver\examples\src\registrar\readme.htmlThis 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.
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
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:
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.
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:
Without these options, debugging will not work.
Installation is now over. Let's test things out to make sure everything works.
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.