Articles
JavaFX Technologies
|
| 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.
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.
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().
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.
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.
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));
}
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().
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);
}
}
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.
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