📄 mar02_ericg.txt
字号:
J 2 M E T E C H T I P S
TIPS, TECHNIQUES, AND SAMPLE CODE
WELCOME to the Java Developer Connection(sm) (JDC)
Java(tm) 2 Platform, Micro Edition (J2ME(tm))
Tech Tips, for March 25, 2002. This issue covers:
* Simple Store-and-Forward Messaging for MIDP Applications
* Compressing XML For Faster Wireless Networking
The J2ME Tech Tips are written by Eric Giguere
(http://www.ericgiguere.com), an engineer at iAnywhere
Solutions, inc. Eric is the author of the book "Java 2 Micro
Edition: Professional Developer's Guide" and co-author of the
book "Mobile Information Device Profile for Java 2 Micro
Edition," both books in John Wiley & Sons' Professional
Developer's Guide series.
You can view this issue of the J2ME Tech Tips on the Web at
http://java.sun.com/jdc/J2METechTips/2002/tt0325.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SIMPLE STORE-AND-FORWARD MESSAGING FOR MIDP APPLICATIONS
Applications written for the Mobile Information Device Profile
(MIDP) are rarely stand-alone applications. Normally, there's
some part of the application that involves calling server-side
code for processing. In MIDP 1.0, the only way to portably invoke
server-side code is by making HTTP requests to a servlet running
in an external Web server. On a wireless device, this can be
quite slow, and so the communication is best done on a background
thread. In fact, it might make more sense to move to a truly
asynchronous communication model, where your application
interacts with other devices by sending and receiving messages.
This Tech Tip shows you how to implement this kind of simple
store-and-forward messaging system using MIDP's persistent record
stores. To run the code in this tip, you need Tomcat or another
servlet-enabled Web server. You can download Tomcat from the
Jakarta Project (http://jakarta.apache.org/tomcat/).
Let's first define the term "message". A message is simply
a class that holds some data, as in this example:
import java.io.*;
public class SimpleMessage implements Persistent {
private String dest;
private Object data;
public SimpleMessage(){
}
public SimpleMessage( String dest, String data ){
setDestination( dest );
setData( data );
}
public SimpleMessage( String dest, byte[] data ){
setDestination( dest );
setData( data );
}
public String getDestination(){
return dest != null ? dest : "";
}
public Object getData(){ return data; }
public void setDestination( String dest ){
this.dest = dest;
}
public void setData( String data ){
this.data = data;
}
public void setData( byte[] data ){
this.data = data;
}
private static final int IS_NULL = 0;
private static final int IS_STRING = 1;
private static final int IS_BINARY = 2;
public byte[] persist() throws IOException {
ByteArrayOutputStream bout =
new ByteArrayOutputStream();
DataOutputStream dout =
new DataOutputStream( bout );
dout.writeUTF( getDestination() );
Object obj = getData();
if( obj instanceof String ){
dout.writeInt( IS_STRING );
dout.writeUTF( (String) obj );
} else if( obj instanceof byte[] ){
dout.writeInt( IS_BINARY );
byte[] arr = (byte []) obj;
dout.writeInt( arr.length );
dout.write( arr );
} else {
dout.writeInt( IS_NULL );
}
dout.flush();
return bout.toByteArray();
}
public void resurrect( byte[] indata )
throws IOException {
ByteArrayInputStream bin =
new ByteArrayInputStream( indata );
DataInputStream din =
new DataInputStream( bin );
dest = din.readUTF();
int type = din.readInt();
if( type == IS_STRING ){
data = din.readUTF();
} else if( type == IS_BINARY ){
int len = din.readInt();
byte[] arr = new byte[ len ];
if( len > 0 ){
din.readFully( arr );
}
data = arr;
} else {
data = null;
}
}
public String toString(){
StringBuffer buf = new StringBuffer();
buf.append( "{destination=\"" );
if( dest != null ) buf.append( dest );
buf.append( "\",data=" );
if( data instanceof byte[] ){
buf.append( "byte array of length " +
((byte[]) data).length );
} else if( data instanceof String ){
buf.append( '"' );
buf.append( (String) data );
buf.append( '"' );
} else {
buf.append( "null" );
}
buf.append( '}' );
return buf.toString();
}
}
The SimpleMessage class holds two things: a destination name
(a string) and some data, which is either a string or a byte
array. The format of the destination name is arbitrary. It could,
for example, be the name of a Java Message Service (JMS) queue.
Or it could be an email address. Later on in this tip you'll see
that a servlet is responsible for interpreting the destination
name.
Notice that the class implements the Persistent interface. This
interface was defined in a previous Tech Tip, "Object
Serialization in CLDC-Based Profiles" (see
http://java.sun.com/jdc/developer/J2METechTips/2002/tt0226.html).
You need this capability to serialize the message for storage and
transport.
The first step in building a simple store-and-forward messaging
system is to build the message hub. The hub is the class that the
application uses to send and receive messages. The hub uses two
record stores, one to track incoming messages, and the other to
track outgoing messages. The messages themselves are sent and
received by a background thread. Using the hub is fairly simple.
The constructor takes two parameters: the URL of a servlet and
a timeout value (in milliseconds) for polling:
String url =
"http://localhost:8080/MessagingServlet";
int timeout = 60000; // every minute
MessageHub hub = new MessageHub( url, timeout );
You then start the hub's background thread:
hub.start();
Now the application can send and receive messages. It sends
messages using the send method:
SimpleMessage msg = new SimpleMessage( "eric",
"Hello!" );
hub.send( msg );
This send method does not block, it serializes the message and
adds it to a record store. The hub's background thread is then
responsible for delivering it.
Receiving a message, however, is a blocking operation:
int timeout = 5000; // wait at most 5 seconds
SimpleMessage msg = hub.receive( timeout );
If the hub receives a message, it returns it immediately.
Otherwise the calling thread is blocked until a message arrives
or the given timeout expires.
To suspend message processing, you can call the hub's stop
method:
hub.stop();
Finally, when the application is about to terminate, it should
destroy to hub in order to free the hub's resources:
hub.destroy();
Let's look at the code for the message hub:
import java.io.*;
import javax.microedition.io.*;
import javax.microedition.rms.*;
/**
* Defines a class that can send and receive
* messages asynchronously. Messages are stored
* in record stores and processed by a background
* thread, which posts them via HTTP to a servlet
* running on an external web server.
*/
public class MessageHub implements Runnable {
// Constructor creates two record stores: one for
// outgoing messages and one for incoming messages.
// Pass in the URL to the servlet and a timeout
// value in milliseconds for how often to poll the
// server if no messages are being sent by the
// client.
public MessageHub( String url, int pullTimeout )
throws RecordStoreException {
inrs = RecordStore.openRecordStore( "mhubin",
true );
outrs = RecordStore.openRecordStore( "mhubout",
true );
this.url = url;
this.pullTimeout = pullTimeout;
}
// Convenience method.
private HttpConnection closeConnection(
HttpConnection conn ){
try {
if( conn != null ){
conn.close();
}
}
catch( IOException e ){
}
return null;
}
// Client calls this to receive a message, passing
// in an appropriate timeout value in milliseconds.
// If the timeout expires, null is returned.
// Otherwise the message is returned.
public SimpleMessage receive( int timeout )
throws IOException, RecordStoreException {
SimpleMessage msg = null;
RecordEnumeration enum = null;
synchronized( inrs ){
try {
if( inrs.getNumRecords() == 0 ){
inrs.wait( timeout );
}
enum = inrs.enumerateRecords( null,
null, false );
if( enum.hasNextElement() ){
int id = enum.nextRecordId();
byte[] rawdata =
inrs.getRecord( id );
SimpleMessage tmp =
new SimpleMessage();
tmp.resurrect( rawdata );
msg = tmp;
inrs.deleteRecord( id );
}
}
catch( InterruptedException e ){
}
finally {
if( enum != null ){
enum.destroy();
}
}
}
return msg;
}
// Client calls this to send a message. The
// message is queued in the outgoing queue and
// the background thread is woken up if necessary.
public int send( SimpleMessage msg )
throws IOException, RecordStoreException {
int id = 0;
synchronized( outrs ){
byte[] rawdata = msg.persist();
id = outrs.addRecord(
rawdata, 0, rawdata.length );
outrs.notify();
}
return id;
}
// The background thread that reads messages
// from the outgoing queue, POSTs them to the
// server and places incoming messages into
// the incoming queue.
public void run(){
HttpConnection conn = null;
boolean checkForMore = true;
while( thread == Thread.currentThread() ){
byte[] record = null;
int recordID = 0;
// First stage: read a record from the
// outgoing store. If there is no record,
// wait for a specific time.
synchronized( outrs ){
try {
if( outrs.getNumRecords() == 0
&& !checkForMore ){
outrs.wait( pullTimeout );
}
RecordEnumeration e =
outrs.enumerateRecords( null,
null, false );
while( e.hasNextElement() ){
recordID = e.nextRecordId();
record =
outrs.getRecord( recordID );
if( record != null ){
outrs.setRecord( recordID,
null, 0, 0 );
break;
} else {
recordID = 0;
record = null;
}
}
e.destroy();
}
catch( RecordStoreException e ){
break;
}
catch( InterruptedException e ){
}
}
// Second stage: POST the record, if any,
// to the web server. If successful,
// delete the record, otherwise restore it.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -