Developing Content with JavaFX Mobile, Java ME, and the Messaging API (JSR 205) - Background

 
By Angela Caicedo, June 2009  

One of the questions every time a new technology appears is "How can I integrate this technology with my existing applications?" This article shows you how to use the JavaFX mobile user interface with existing Java ME APIs and, in particular, the Messaging API (JSR 205). You will build two applications: one will create and send the message, and the second application will receive the message.

Wireless Messaging API

The WMA is an optional package based on the Generic Connection Framework (GCF) and targets the Connected Limited Device Configuration (CLDC) as its lowest common denominator, meaning that it can extend both CLDC- and CDC-based profiles. It thus supports Java Platform, Mobile Edition (Java ME) applications targeted at cell phones and other devices that can send and receive wireless messages.

 

Message Interface

The interface javax.wireless.messaging.Message is the base for all types of messages communicated using the WMA - a Message is what is sent and received, produced and consumed.

In some respects, a Message looks similar to a Datagram: it has source and destination addresses, a payload, and ways to send and block for a message. The WMA provides additional functionality, such as support for binary and text messages and a listener interface for receiving messages asynchronously.

The WMA defines two subinterfaces, BinaryMessage and TextMessage, and the specification is extensible, to allow for support of additional message types.

How messages (and related control information) are encoded for transmission is protocol-specific and transparent to the WMA. If you're interested in such low-level details, refer to the WMA specification, which lists the standard documents.

BinaryMessage Interface

The BinaryMessage subinterface represents a message with a binary payload, and declares methods to set and get it. General methods to set and get the address of the message and get its time stamp are inherited from Message.

TextMessage Interface

The TextMessage subinterface represents a message with a text payload, such as an SMS-based short text message. The TextMessage interface provides methods to set and get text payloads (instances of String). Before the text message is sent or received, the underlying implementation is responsible for properly encoding or decoding the String to or from the appropriate format, for example GSM 7-bit or UCS-2. General methods to set and get the address of the message and get its time stamp are inherited from Message.

MessageConnection Interface

The MessageConnection interface is a subinterface of the Generic Connection Framework's javax.microedition.io.Connection. At the top of the next figure you can see the GCF, and at the bottom you can see how the MessageConnection interface relates to the rest of the WMA.

 

MessageConnection provides the methods newMessage(), send(), and receive(), to create, send, and receive Message objects respectively. This interface also defines two String constants, BINARY_MESSAGE and TEXT_MESSAGE, one of which is passed to the newMessage() factory method to create the appropriate Message object type.

As with any other GCF connection, to create a Connection (in this context a MessageConnection) call javax.microedition.io.Connector.open(), and to close it call javax.microedition.Connection.close(). You can have multiple MessageConnections open simultaneously.

A MessageConnection can be created in one of two modes: as a client connection or as a server connection. As a client it can only send messages, while server connections can both send and receive messages. You specify a connection as either client or server the same way as when making other GCF connections, by way of the URL. A URL for a client connection includes a destination address, as in:

(MessageConnection)Connector.open("sms:// +5121234567:5000");

And a URL for a server connection specifies a local address (no host, just a protocol-specific local address, typically a port number), as in:

(MessageConnection)Connector.open("sms:// :5000");

Trying to bind to an already reserved local address causes an IOException to be thrown.

The URL scheme for creating a MessageConnection is not specific to a single protocol - instead it is intended to support many wireless protocols. Which protocols are considered "wireless messaging protocols" depends on the protocol adapters included in the implementation. The WMA specification defines the following protocol adapters:

  • " sms" for Short Messaging System. SMS is bi-directional: you can create, send, and receive messages (and thus support both client and server connections).

  • " cbs" for Cell Broadcast Short Message. CBS messages are broadcast by a base station, and can only be received. Attempting to send on a cbs connection results in an IOException.

Vendors can implement additional protocol adapters and if necessary new Message subinterfaces. The WMA RI supports both SMS and CBS using transport emulation.

MessageListener Interface

The MessageListener implements the Listener design pattern for receiving Message objects asynchronously; that is, without blocking while waiting for messages. This interface defines a single method; notifyIncomingMessage() is invoked by the platform each time a message is received. To register for messages, use the MessageConnection.setListener() method, as you'll see later in the article. Because some platform implementations may be single-threaded, the amount of processing within notifyIncomingMessage() should (more than should, must) be kept to a minimum. You should dispatch a separate thread to consume and process the message, so the platform spends as little time as possible in notifyIncomingMessage().

Using the WMA: Sending and Receiving Messages

The WMA is a really well designed, very simple-to-use API. Let's go over some code examples that show how to take advantage of it.

Creating a MessageConnection

First we define a helper method that returns a MessageConnection. To create a client MessageConnection just call Connector.open(), passing a URL that specifies a valid WMA messaging protocol and that includes either the local address for a server connection, or a destination address for a client connection.

Listing 1: The newMessageConnection() Helper Method
/**
 *  newMessageConnection returns a new MessageConnection
 *  @param addr is the address (local or remote)
 *  @return MessageConnection that was created (client 
 *  or server)
 *  @throws Exception if an error is encountered
 */

public MessageConnection newMessageConnection(String addr)
  throws Exception {
    return((MessageConnection)Connector.open(addr));
}
       
 

Creating and Sending a TextMessage

Then we define a helper method that creates and sends a TextMessage. Note that if the connection is a client, the destination address will already be set for you by the implementation (the address is taken from the URL that was passed when the client connection was created), but if it is a server connection, you must set the destination address. In the following helper method, a non-null URL indicates that the caller wants to set the destination address before sending. Before sending the text message, the method populates the outgoing message by calling setPayloadText().

Listing 2: The sendTextMessage() Helper Method
/**
 *  sendTextMessage sends a TextMessage on the specified
 * connection
 *  @param mc the MessageConnection
 *  @param msg the message to send
 *  @param url the destination address, typically used in
 *  server mode
 */
public void sendTextMessage(MessageConnection mc, String 
  msg, String url) {
    try {
        TextMessage tmsg =
            (TextMessage)mc.newMessage
              (MessageConnection.TEXT_MESSAGE);
        if (url!= null)
            tmsg.setAddress(url);
        tmsg.setPayloadText(msg);
        mc.send(tmsg);
    }
    catch(Exception e) {
        //  Handle the exception...
        System.out.println("sendTextMessage " + e);
    }
}
       
 

Waiting for a Message on a MessageConnection

Recall that only a server MessageConnection can receive messages. To wait for messages you normally dispatch a thread during initialization. This thread invokes Message.receive() to block on and consume messages. Once a message is received you must determine its type (text, binary, or other) so that you can call the appropriate get-payload method.

Let's look at the class MessageProcessor, which implements a thread of execution that waits for and processes messages.

Listing 3: The MessageProcessor Class
/**
 *  Thread of execution responsible of receiving and 
 *  processing messages
 */
class MessageProcessor implements Runnable {

    Thread th = new Thread(this);
    MessageConnection mc; // MessageConnection to handle
    boolean done; // if true, thread must exit

    /**
     * Constructor for multi-pass MessageProcessor
     * @param mc is the MessageConnection for this message 
     * processor
     */
    public MessageProcessor(MessageConnection mc) {
        this.mc = mc;
        th.start();
    }

    /**
     * Constructor for single-pass MessageProcessor
     * @param mc is the MessageConnection for this message 
     * processor
     * @param singlepass if true this processor must 
     * process only one 
     *  message and exit
     */
    public MessageProcessor(MessageConnection mc) {
        this.mc = mc;
        th.start();
    }

    /** Notify the message processor to exit */
    public void notifyDone() {
        done = true;
    }

    /** Thread's run method to wait for and process 
     *  received messages.
     */
    public void run() {
        processMessage();
        
    }

    /** processMessage reads and processes a Message */
    public void processMessage() {
        Message msg = null;
        //  Try reading (maybe block for) a message
        try {
            msg = mc.receive();
        }
        catch (Exception e) {
            // Handle reading errors
            System.out.println("processMessage.receive " + e);
        }
        // Process the received message
        if (msg instanceof TextMessage) {
            TextMessage tmsg = (TextMessage)msg;
            //  Handle the text message...
            ticker.setString(tmsg.getPayloadText());
        }
        else {
            // process received message
            if (msg instanceof BinaryMessage) {
                BinaryMessage bmsg = (BinaryMessage)msg;
                byte[] data = bmsg.getPayloadData();
                //  Handle the binary message...
            }
            else {
                //  Ignore
            }
        }
    }
} // MessageProcessor
       
 

Back to top