Sun Java Solaris Communities My SDN Account Join SDN
 
Technical Articles and Tips

The J2ME Wireless Toolkit WMA Console

 



Version 2.0 of the J2ME Wireless Toolkit offers many new features and improvements to wireless developers. One new feature is support for the Wireless Messaging API (WMA). The WMA defines an application programming interface (API) for sending and receiving short text or binary messages over wireless connections. The WMA currently supports the SMS (Short Message Service) and CBS (Cell Broadcast Short Message Service) protocols used by mobile telephone networks.

Although the WMA package is not complicated, no commercially available J2ME-enabled device yet supports it. New devices are on their way to market, including many that will support version 2.0 of the Mobile Information Device Profile (MIDP), but the present lack of devices makes it hard to create and test real message-based applications. Testing is possible using the WMA Reference Implementation (RI) with an appropriate emulator, but it's not a complete solution. The toolkit's new WMA Console, however, provides a complete development and testing environment for WMA-based applications.

To use the console, start the toolkit's Preferences application, choose the API Availability tab, and make sure that the Wireless Messaging API is checked. Now run the Utilities application and on the right side of the window press the Open Console button. (You can also get to the Preferences and Utilities applications from menu items in the KToolbar application.)

The console emulates a simple wireless messaging device. Each console instance - you can run more than one console at a time - is identified in its title bar by a unique number, referred to as its phone number to reflect the way a real device works. The first console is assigned the number 5550000 by default. If you immediately open a second console, it is assigned the number 5550001, and so on. You can override these defaults: in the Preferences application, choose the WMA page, and edit the First Assigned Phone Number and Phone Number of Next Emulator fields.

To send a message from a console, press either the Send SMS or Send CBS button. The resulting dialog has two pages, one for text messages and one for binary messages. The SMS dialog lists the numbers of all console and emulator instances (collectively referred to as clients) currently running. Select the desired clients, enter the port number to which the message is to be sent, and either type the text message or select the binary file you want to send, then press the Send button. The CBS dialog is a bit simpler since the message will be broadcast to all known clients - supply a message identifier (a number) and the text or file to send, and press the Send button.

Note well: If no port number is specified in a message sent to a real SMS device, the device's default message-handling system handles the message - shows it to the user, for example. WMA applications cannot receive or otherwise intercept such messages: You must specify a port number.

The console's main window lists the messages it sends and receives. You can use two console instances to chat with yourself, but it's much more interesting to involve an application in the conversation, so that you can verify that the application is sending and receiving messages correctly. The following MIDlet waits for text messages to arrive at a given port (it prompts you for the port on startup) and sends it back to the sender with the text reversed:

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import javax.wireless.messaging.*;

// A WMA test application that opens a server
// connection on the specified port and waits
// for text messages to arrive. It then sends
// the reversed text message back to the client.

public class WMATest extends MIDlet
implements CommandListener,
MessageReceiver,
Runnable {

// The application data

private Display                 display;
private String                  port = "1000";
private Form                    setupForm;
private TextField               portTF;
private Form                    statusForm;
private MessageConnection       currentConn;
private MessageReceiverListener messageListener;

// Our commands

public static final Command exitCommand =
new Command( "Exit",
Command.EXIT, 1 );

public static final Command okCommand =
new Command( "OK",
Command.OK, 1 );

public static final Command resetCommand =
new Command( "Reset",
Command.SCREEN, 1 );

public WMATest(){
}

// Adds a message to the status form.

public void addStatus( String msg ){
statusForm.append( msg );
}

// Handles the screen events.

public void commandAction( Command c,
Displayable d ){
if( c == exitCommand ){
exitMIDlet();
} else if( c == okCommand ){
port = portTF.getString();
new Thread( this ).start();
} else if( c == resetCommand ){
closeConnection();
getDisplay().setCurrent( setupForm );
}
}

// Clears the status form.

private void clearStatus(){
int s;

while( ( s = statusForm.size() ) > 0 ){
statusForm.delete( s-1 );
}

addStatus( "Waiting for messages on port " +
port );
}

// Closes the current server connection, if any.

private void closeConnection(){
if( currentConn != null ){
try {
currentConn.close();
}
catch( IOException e ){
}

currentConn = null;
}
}

// Called by system to destroy the MIDlet.

protected void destroyApp( boolean unconditional )
throws MIDletStateChangeException {
exitMIDlet();
}

// General cleanup.

public void exitMIDlet(){
if( messageListener != null ){
messageListener.stop();
messageListener = null;
}

closeConnection();
notifyDestroyed();
}

// Returns the MIDlet's display object.

public Display getDisplay(){ return display; }

// Handles errors simply using an alert to
// display the exception.

private void handleError( Throwable e, Displayable next ){
Alert a = new Alert( "Exception",
e.toString(),
null,
AlertType.ERROR );
a.setTimeout( Alert.FOREVER );
getDisplay().setCurrent( a, next );
}

// Initializes the MIDlet by creating the two forms
// as well as the message listener.

protected void initMIDlet(){
messageListener = new MessageReceiverListener( this );

setupForm = new Form( "Receiver Setup" );
setupForm.addCommand( exitCommand );
setupForm.addCommand( okCommand );
setupForm.setCommandListener( this );

portTF = new TextField( "Port number:", port,
5, TextField.NUMERIC );

setupForm.append( portTF );

statusForm = new Form( "Status" );
statusForm.addCommand( resetCommand );
statusForm.addCommand( exitCommand );
statusForm.setCommandListener( this );

getDisplay().setCurrent( setupForm );
}

// Implements the MessageReceiver interface. Called
// when an error occurs while receiving a message.

public void messageError( MessageConnection conn,
Throwable excep ){
handleError( excep, statusForm );
}

// Implements the MessageReceiver interface. Called
// when a message has been successfully received.

public void messageReceived( MessageConnection conn,
Message msg ){
if( messageListener == null ) return;
if( conn != currentConn ) return;

if( msg instanceof BinaryMessage ){
addStatus( "Ignoring binary message" );
return;
}

TextMessage tmsg = (TextMessage) msg;
String text = tmsg.getPayloadText();
String addr = tmsg.getAddress();

addStatus( "Received " + text );

if( addr == null ){
addStatus( "No return address, ignoring" );
return;
}

if( text != null ){
StringBuffer b =
new StringBuffer( text.length() );
b.append( text );
b.reverse();
tmsg.setPayloadText( b.toString() );

addStatus( "Sending reverse to " + addr );

try {
conn.send( tmsg );
}
catch( IOException e ){
handleError( e, statusForm );
}
}
}

// Called by the system to pause the MIDlet.

protected void pauseApp(){
}

// Creates the server connection. This needs to run in
// a separate thread to avoid blocking the user interface,
// since the device may prompt the user to confirm
// that the application has permission to use wireless
// connectivity.

public void run(){
closeConnection();

String url = "sms://:" + port;

try {
// Create the connection and register it with
// the message listener.

currentConn = (MessageConnection)
Connector.open( url );
currentConn.setMessageListener( messageListener );

// Move to the status form.

getDisplay().setCurrent( statusForm );
clearStatus();
}
catch( IOException e ){
handleError( e, setupForm );
}
}

// Called by the system to activate the MIDlet.

protected void startApp()
throws MIDletStateChangeException {
if( display == null ){
display = Display.getDisplay( this );
initMIDlet();
}
}
}

This MIDlet depends on the MessageReceiver interface and the MessageReceiverListener class to receive the messages. Notice that the MIDlet spawns a thread to create the server connection, so as to avoid the user-interface deadlock that can occur if the device prompts the user for permission to send and receive SMS messages.

For more sample code, see the SMSDemo example that ships with the J2ME Wireless Toolkit.


Back To Top