| Page 3 of 3
|
|
XML Tools for Information Appliances
The potential for SOAP on lightweight clients is big. SOAP is notoriously
slow and resource-intensive compared to RPC, CORBA, and JNI calls, but with more
powerful information appliances becoming available each day, this hopefully
won't be an issue soon. Simple and relatively infrequent calls, such as requests
for weather or stock data from HTTP SOAP servers, are certainly feasible with
today's information appliances.
There are currently two lightweight Java SOAP libraries that are worth
mentioning:
In this section, we'll show you how to install the Java KVM and Palm OS
Emulator on your machine. Then we'll bring together some of the different
technologies we've talked about in this chapter - Java and XML on a Palm OS
device. We'll create a complete lightweight Java XML application using these
tools:
- Java KVM for J2ME - the Java Kilobyte Virtual Machine (KVM) is
designed to operate with as little as 160 to 512 KB of total memory. See Java
KVM (page 572).
- NanoXML - a lightweight DOM-style XML parser and document
generator. See NanoXML. (page 577)
- Palm OS Emulator (POSE) - software that emulates the hardware of
different Palm devices (Palm III, Vx, VII, etc.). This will be our unit
testing platform. See Palm OS Emulator (page 620).
The application allows data entry of contact information into a custom Palm
database we'll call the contact book. The term database in Palm OS lingo
is rather deceiving as there is no way to create and drop tables on the
database. It's better to think of a Palm OS database as permanent data residing
in a section of non-volatile memory. The section is comprised of variable-length
records. Each record is a variable-length array of bytes. There aren't tables,
columns, or rows in a Palm OS database at all. Nevertheless, database is
standard terminology.
A Palm OS "database" is not a database in the traditional sense.
Rather, it is a persistent store comprised of any number of variable-length
records. Each record is a variable-length array of bytes up to 64KB.
Our application will beam contact information from a custom Palm database
(called the "contact book") to the same database on another Palm OS
device. Data will be packaged as an XML document on the sender's side, and
received and parsed on the receiver's side. The user will then be prompted to
accept the data into his contact book or not. If accepted, XML will be stored
natively as a new record in the contact book database. The application also
provides a mechanism to enter, search, view, and edit contact entries in the
contact book database.

In this section, we'll walk through the setup and installation of the Java
KVM and POSE.
First, download the J2ME CLDC v1.0.2 (see CLDC, page 571) and the
kjava overlay from:
http://www.sun.com/software/communitysource/j2me/cldc/download.html
Unzip j2me_cldc-1_0_2-fcs-winunix.zip first. Then unzip
j2me_cldc-1_0_2-fcs-kjava_overlay.zip using the same directory base in which you
unzipped the first file. Overwrite any existing files.
The first archive contains the CLDC reference implementation for Win32,
Solaris, and Linux. It includes source code, runtimes, tools, and documentation
for porting the CLDC to other platforms. This archive is designed to be used
with a profile (see Profiles, page 571).
Since there is no PDA Profile implementation yet (see PDA Profile,
page 571), we'll use the CLDC-compatible port for the Palm contained in the
overlay archive. This is the second archive you downloaded, the kjava overlay.
The overlay installs Palm-specific tools, samples, the all-important package
com.sun.kjava (see Package com.sun.kjava, page 572), and KVM.prc - the
Java KVM packaged as a Palm application.
The overlay is not intended to be used alone; it must be installed
after and on top of the first archive, overwriting any existing files.
We'll use the POSE as our testing platform as we write our code. The POSE
runs on Win32, Solaris, or Linux (although you could compile it for another
platform as the source code is available). Remember our application will beam
addresses from one Palm OS device to another. Obviously, the POSE doesn't have
an infrared port since it's just software. So to test the application fully you
must have two Palm OS devices (Handspring Visors, Sony Clié or IBM Workpads are
fine).
Step 1 - Install the Emulator
First, download the emulator from http://www.palmos.com/dev/tech/tools/emulator/.
It is available for Windows (as a ZIP file), Macintosh (as a SIT file), and for
UNIX (source code in a TAR that you must compile). Extract the file.
Step 2 - Obtain a ROM Image
Before the emulator will operate, it needs a binary image of the environment
to be emulated. The base installation doesn't come with any ROM images. If you
own a Palm OS device, you can download an image of that device's ROM using the
emulator. Simply run emulator.exe, which was installed in Step 1, and select
"Download a ROM image from a Palm OS device." Detailed instructions
are then given; it's really quite a simple process.
If you don't own a Palm OS device, you can download a ROM image from the Palm
web site (http://www.palmos.com/dev/tech/tools/emulator/#roms).
You'll need to join the Palm Alliance Program, which simply requires you to fill
out an online form.
Step 3 - Run the Emulator for the First Time
Now you should run emulator.exe, which was installed in Step 1. Click the new
button, Start a new emulator session. Under ROM file, select Other and navigate
to the .ROM file you obtained in Step 2. The device field should automatically
populate, but if it doesn't, select the correct Palm device type. Leave skin as
generic. Finally, select the RAM size that corresponds to the actual RAM of the
device you are emulating (for example, if emulating a Palm Vx, you should set
this value to 8192KB, click OK. The emulator will then start.
Step 4 - Install the Java KVM On the Emulator
Now we must load the virtual machine onto the emulator. Right-click anywhere
on the emulator. From the pop-up menu, select Install Application/Database and
Other. Navigate to j2me_cldc/bin/kjava/palm and select KVM.prc. The virtual
machine will be installed. Now do the same for j2me_cldc/bin/kjava/palm/KVMutil.prc.
KVMutil is a debugging utility that allows you to set the maximum heap size and
screen output (System.out) options.
Contact Classes
The contact beamer application is really four applications in one. We have a
"sender" application which is invoked to list and beam contact book
entries. We also have a "receiver" application, which is invoked to
listen for documents sent by the sender. A search tool allows the user to
view and edit entries. Finally, a contact adder allows the user to insert new
contact entries into the contact book. The deletion of individual contact
entries in the database is left as an exercise to the reader! However, the
entire contact book database can be deleted using the standard means to delete a
.PDB/.PRC from the unit.
The application is comprised of five major classes:
- Class ContactBook
- The "main menu" of the application, this class simply
displays four buttons to the user: add contacts, beam contacts, receive
contacts, and quit.
- One of the next three classes are instantiated when a button is
selected
- Class AddContact
- Displays a form into which the user enters information for a single
contact: first name, last name, address, city, state/province, zip/postal
code, country, phone number.
- Allows the user to save the information as a new entry (or record)
in the contact book database.
- The ability to quickly enter multiple records is provided by Class BeamContact
- Displays contact book entries (or records) to the user
- Prompts the user to select an entry to beam
- Builds a Contact XML document instance from the selected entry
using NanoXML
- Beams the document to another device
- Class ReceiveContact
- Displays a Waiting for beamed data message to the user
- Blocks until a contact XML document is received
- Prompts the user to import the contact entry into the local contact
book database
- Creates a new record in the contact book database if user chooses
to keep the information
- Class SearchContact
- Displays a text field, asking for the user to display the search
criteria
- Searches all eight fields of each record in the database for the
specified criteria
- Displays matching records as buttons whose text is the first and
last name of each matching contact
- Waits for the user to press one of the buttons, then displays that
contact entry and allows the user to modify it in the database
Other classes are used by these five classes, but those classes are from the
NanoXML library discussed in a previous section of this chapter. We'll discuss
the first four classes in the chapter; for brevity's sake we'll leave
examination of class SearchContact to the reader. So now let's take a look at
the only class with a main() method, ContactBook.
ContactBook
public class ContactBook
extends com.sun.kjava.Spotlet
ContactBook is the "main menu" of the application. It must
extend com.sun.kjava.Spotlet to fit into the package com.sun.kjava application
paradigm.
Spotlet is conceptually similar to java.applet.Applet in that it is a class
for handling event callbacks. Overriding one of the event handlers, such as
penMove(), keyDown(), or keyUp(), will enable the extended class (BeamAddress,
in this case) to receive notification of these events.
ContactBook is quite simple. It displays five buttons to the user, then waits
for user input. When one of the buttons is pressed, the flow of execution is
handed off to class AddContact, class BeamContact, class ReceiveContact, class
SearchContact, or it is terminated (if quit is selected). Here is a screenshot
so you get a feel for how it looks:

We'll examine three parts of class ContactBook:
- The constructor
- Painting the screen
- Handling penDown() events
Constructor
The constructor allocates objects for the com.sun.kjava.Button variables
declared with private class scope:
public class ContactBook extends Spotlet {
private Button exitBtn, addBtn, beamBtn, receiveBtn;
public ContactBook() {
exitBtn = new Button("Quit", 135, 140);
addBtn = new Button("Add Contacts", 30, 50);
beamBtn = new Button("Beam Contacts", 30, 70);
receiveBtn = new Button("Receive Contacts", 30,
90);
searchBtn = new Button("Search/View/Edit
Contacts", 30,
90);
paint(); //paint the screen
}
The Button constructor's three arguments are the button's text, and x and y
screen coordinates in pixels. Most Palm OS devices have screens with 160x160
pixels, although Sony's new PEG-N710C doubles this to 320x320. We are assuming a
160x160 screen.
The last instruction in the constructor calls the paint() method, which
brings us to our next topic.
Let's take a look at the paint() method, which is called to clear and paint
the screen:
protected void paint() {
Graphics g = Graphics.getGraphics();
g.clearScreen(); //clear the screen
Unlike other GUI environments you might be used to, which may have multiple
graphics objects and device contexts, there is only one and it's
com.sun.kjava.Graphics object available to all Spotlets. It is a static
singleton object in class Graphics. with this object that all screen painting is
done. The above code cleared the screen; now let's draw a title and buttons:
//draw the title
g.drawString("What would you like to do?", 30,
10);
//draw the buttons
exitBtn.paint();
addBtn.paint();
beamBtn.paint();
receiveBtn.paint();
searchBtn.paint();
}
Graphics.drawString() is pretty self-explanatory. The first argument is the
string to paint, while the next arguments are the x and y screen coordinates,
respectively, in pixels. The Button.paint() method simply paints the button on
the screen with its defined button text and screen coordinates.
By extending com.sun.kjava.Spotlet, ContactBook is able to receive a variety
of user interface events by overriding Spotlet event handlers. In this case, we
are only interested in knowing when the user has clicked one of our buttons.
We'll override Spotlet.penDown() to determine this.
public void penDown(int x, int y) {
//exit btn pressed?
if (exitBtn.pressed(x,y))
System.exit(1);
//add btn pressed?
else if (addBtn.pressed(x,y)) {
AddContact ac = new AddContact(this);
}
//beam btn pressed?
else if (beamBtn.pressed(x,y)) {
BeamContact ba = new BeamContact(this);
}
//receive btn pressed?
else if (receiveBtn.pressed(x,y)) {
ReceiveContact ra = new ReceiveContact(this);
}
//search btn pressed?
else if (searchBtn.pressed(x,y)) {
SearchContact sc = new SearchContact(this);
}
}
Spotlet.penDown() receives the x and y screen coordinates, in pixels, where
the stylus was pressed. The rest of the code calls com.sun.kjava.Button.pressed(),
which returns Boolean true if (x,y) are within the Button boundaries, to
determine if a Button was pressed. If a Button was pressed, one of the other
user interface classes is called. We will discuss each of these in detail. The
only exception is the quit Button, which terminates the program.
Let's take a look at the class, AddContact, that gets instantiated if the
top-most Button is pressed.
AddContact
public class AddContact
extends com.sun.kjava.Spotlet
AddContact is the class that enables the user to add contact entries into the
custom Palm database, JavaXMLProgRef. A custom Palm database, also called a
user-defined database, is a database that is not one of the system-defined
databases: the address, date book, memo pad, and to do list databases. We have
chosen not to use the address database because it defines its own record format
for contacts; we would not be able to natively store XML if we used it. Creating
our own user-defined database allows us to store bytes in any format we choose,
and we choose XML, of course!
AddContact displays a data entry form to the user for the following fields:
- First name
- Last name
- Address
- City
- State/province
- Zip/postal code
- Country
- Phone number
Three buttons are also displayed:
- quit - return control to class ContactBook without saving the data
entered (if any) in the form
- save & quit - creates a new record in the JavaXMLProgRef
database using current form values, then returns control to ContactBook and
its screen
- save & continue - creates a new record in the JavaXMLProgRef
database using current form values, then erases all form field values so
another contact can be entered.
Let's look at a screenshot:

We'll examine three parts of classAddContact:
- The constructor
- Saving the generated XML as a new record in the JavaXMLProgRef database
- Generating XML from the form's eight com.sun.kjava.TextField objects
Constructor
The constructor allocates objects for the com.sun.kjava.Button and
com.sun.kjava.TextField variables declared with private class scope. It also
registers as a listener for user interface events via the com.sun.kjava.Spotlet
event handlers.
public class AddContact extends Spotlet {
private Button quitBtn, saveQuitBtn, saveAddMoreBtn;
private ContactBookSpotlet _owner;
private TextField fname, lname, address, city, state, zip,
country, phone;
public AddContact(ContactBookSpotlet owner) {
//register for events
register(NO_EVENT_OPTIONS);
_owner = owner;
quitBtn = new Button("Quit", 1, 140);
saveQuitBtn = new Button("Save&Quit", 33,
140);
saveAddMoreBtn = new Button("Save&Continue",
92, 140);
fname = new TextField("First Name", 5, 10, 150,
10);
lname = new TextField("Last Name", 5, 25, 150,
10);
address = new TextField("Address", 5, 40, 150,
10);
city = new TextField("City", 5, 55, 150, 10);
state = new TextField("State", 5, 70, 150, 10);
zip = new TextField("Zip", 5, 85, 150, 10);
country = new TextField("Country", 5, 100, 150,
10);
phone = new TextField("Phone", 5, 115, 150, 10);
paint(); //paint the screen
}
The TextField constructor's five arguments are similar. The first is the text
or title that gets put in front of the text field (a colon is automatically
appended to the supplied java.lang.String by the TextField class). The next four
elements are measured in screen pixels and are, respectively, the upper (x,y)
coordinates of the TextField, and the width (length) and height of the TextField.
It's interesting to note that this simple UI element does not scroll if
characters are entered after the full width of the element is occupied.
Therefore, the maximum number of characters held by a TextField is determined by
its width. Another oddity is that the Palm OS's use of a non-fixed-width font
makes this maximum character length vary based upon the width of the characters
entered (for example, less m's can be entered than l's)!
The last instruction in the constructor calls the paint() method, which we
will skip since it is very similar to ContactBook.paint() (see Painting the
Screen, page 623).
Saving the Form as XML
If Save & Quit or Save & Continue are clicked, the penDown() event
handler
(see Handling penDown() Events, page 624) routes processing to the
save() method. This method has the responsibility for:
- Opening the JavaXMLProgRef database
- Creating the JavaXMLProgRef database if it doesn't already exist
- Writing a new record at the end of the JavaXMLProgRef database
- Closing the JavaXMLProgRef database
The name "database" is really a misnomer, as the Palm OS provides
no way to create and drop tables on a "database." It's better to think
of a Palm OS database as a section of permanent data resident in non-volatile
memory. The section is comprised of variable-length records. Each record is a
variable-length array of bytes. Since we're using a user-defined database, not a
system-defined database like the address book, that means the possibility exists
that JavaXMLProgRef doesn't exist on the system yet. In that case, we'll create
a new, empty database. If it does exist, we'll open the existing one. Then we'll
write a record at the end of the database and close it. Let's look at the code.
private void save() {
Database db = new Database(ContactBook.creatorId,
ContactBook.type, Database.WRITEONLY);
if (!db.isOpen()) {
//the database doesn't exist yet. create it.
Database.create(0, ContactBook.dbName,
ContactBook.creatorId, ContactBook.type,
false);
db = new Database(ContactBook.creatorId,
ContactBook.type, Database.WRITEONLY);
}
if (db.isOpen())
db.addRecord(getContactAsXML().getBytes());
db.close();
}
}
ContactBook.creatorId is an int (actually, the JNI call to the Palm OS
requires the C/C++ unsigned long datatype, but that's handled for you). It
identifies the application, company, or individual who created this database.
When the user deletes an application in his Palm, all Palm databases (PDBs) with
the same creatorId as the application get deleted also. To register your own
creatorId, go to http://www.palmos.com/dev/tech/palmos/creatorid/. I've
registered 0x4455434B for this application, so we'll use that throughout.
ContactBook.creatorType is also an int (actually, the JNI call to the Palm OS
requires the C/C++ unsigned long datatype, but that's handled for you). It is
used to distinguish between multiple databases with different types of
information in them. I've chosen to use 0x32132132.
ContactBook.dbName is a java.lang.String that names our database. It
must be unique for all database names on the device. In this case, we have named
it JavaXMLProgRef. To truly ensure uniqueness, though, Palm Developer Support
recommends using a name, followed by a hyphen, followed by your unique creatorId.
com.sun.kjava.Database.addRecord() creates a new record at the end of the
database. Its content is set to the XML document, as bytes. All records must be
passed as bytes to com.sun.kjava.Database.addRecord().
Part of adding a record in the database is a call to
AddContact.getContactAsXML(). Let's look at that method now.
Since we store XML natively in our JavaXMLProgRef database, we need to
generate an XML document from the form fields as filled out by the user. The
method which does this is AddContact.getContactAsXML(). Let's take a look at it:
private String getContactAsXML() {
//construct an XML document from the form fields.
//return it as a string
kXMLElement root = new kXMLElement();
root.setTagName("contact");
root.addProperty("fname", fname.getText());
root.addProperty("lname", lname.getText());
root.addProperty("address", address.getText());
root.addProperty("city", city.getText());
root.addProperty("state", state.getText());
root.addProperty("zip", zip.getText());
root.addProperty("country", country.getText());
root.addProperty("phone", phone.getText());
return root.toString();
}
Here we use class nanoxml.kXMLElement from the NanoXML CLDC/KVM port to build
a document. kXMLElement has the same methods as nanoxml.XMLElement. This method
builds a simple XML document instance using com.sun.kjava.TextField.getText().
getText() returns the text the user entered into a TextField object.
Finally, getContactAsXML() returns the built document as a string. Here is a
document instance:
<contact fname="Frantic" lname="Neumann"
address="123 Fake
Street" city="Denver"
state="Colorado" zip="80202" country="USA"
phone="303-303-3000"/>
BeamContact
public class BeamContact
extends com.sun.kjava.Spotlet
implements
com.sun.kjava.DialogOwner
BeamContact is the core class for sending a contact to the ReceiveContact
class. It must extend com.sun.kjava.Spotlet to fit into the package
com.sun.kjava application paradigm.
Spotlet is conceptually similar to java.applet.Applet in that it is a class
for handling event callbacks. Overriding one of the event handlers, such as
penMove(), keyDown(), or keyUp(), will enable the extended class (BeamContact,
in this case) to receive notification of these events.
BeamContact behaves similarly to a web search engine result page. When you
search for "apples" in a search engine, the result set matching
"apples" is typically much larger than what you want displayed in your
browser. So search engines usually include Next and Previous hyperlinks,
allowing you to scroll through the result set corresponding to a search for
"apples." Similarly, BeamContact displays six contact book records per
page, with means to scroll through the result set.
BeamContact displays a preset number of buttons on a "page." Each
button has text corresponding to the first and last name of a record. Clicking
one of these buttons begins the document building and beaming process. Note that
in this simple application; only one contact can be beamed at a time.
Here's a screenshot of the application so you get a feel for how it works:

We'll examine three parts of class BeamContact:
- Painting the screen
- Reading from the contact book database, JavaXMLProgRef
- Beaming the document
Painting the Screen and Reading the Contact Book
Let's take a look at the paint() method, which is called to clear and paint
the screen:
private void paint() {
Graphics g = Graphics.getGraphics();
Unlike other GUI environments you might be used to, which may have multiple
graphics objects and device contexts, there is one and only
com.sun.kjava.Graphics object available to all Spotlets. It's with this object
that all screen painting is done.
g.clearScreen(); //clear the screen
//draw exit button and title
exitBtn.paint();
g.drawString("Select Contact to Beam", 35, 0);
Now we open the contact book database:
int numRecords = 0;
try {
Database db = new Database(0x44415441, 0x61646472,
com.sun.kjava.Database.READONLY);
if (db == null)
System.out.println("Error opening
contact book");
With the database open for read access, we're ready to read contact book
records and display a page
of buttons.
The first step is to determine, based on the current record being viewed, how
many buttons we'll need for this page. When the user is viewing the 10th-15th
records with 17 records in the database, and the user selects the Next button,
we only need two buttons for the new page. We'll save this value in numEntryBtns,
defined with class scope:
private int numEntryBtns = 0;
We need this value to have class scope so that when a penDown() event is
handled, we know how buttons are being displayed on the current page.
To calculate numEntryBtns, we also need to store the number of records in the
database. We'll call this numRecords. We also use this value to determine
whether a Next button should be drawn at the bottom of a page. numRecords only
needs method scope:
int numRecords = 0;
ENTRIES_PER_PAGE is the constant that determines how many records are
displayed per page. It is also defined with class scope. You can change this
value to easily vary how many buttons (contact entries) are displayed per page:
private final static int ENTRIES_PER_PAGE = 6;
We chose six because six fit nicely on a 160x160 pixel screen without looking
cramped. If you have a 320x320 pixel Palm OS device, such as the Sony PEG-N710C,
you should be able to double this value.
Each record's first and last name are displayed in a button. These buttons
are defined with class scope so they are accessible in both penDown() and
paint(). In penDown(), we ask each button individually if it was pressed() (see Handling
penDown() Events, page 624). The buttons are stored in entryBtns
private Button[] entryBtns;
Here's the code to allocate the number of buttons we'll need for this page:
//get at most ENTRIES_PER_PAGE records, but less if
there aren't that many records left
numRecords = db.getNumberOfRecords();
numEntryBtns = Math.min(ENTRIES_PER_PAGE, numRecords
-
currentRecord);
entryBtns = new Button[numEntryBtns+1];
Now we can read first and last names in each record, and display the
allocated buttons with those names. First, let's look at the currentRecord
variable, defined with class scope:
private int currentRecord = 0;
This integer represents which record in the database is currently being
displayed at the top of the page. It's used in the penDown() method to determine
which, if any, of the contact buttons were pressed. In our paint() method,
however, we use currentRecord to draw the next or previous page of buttons:
for (int j=currentRecord; j<currentRecord + numEntryBtns;
j++) {
byte[] buf = db.getRecord(j);
kXMLElement elem = new kXMLElement();
elem.parseString(new String(buf));
String lname = elem.getProperty("lname"), //last name
fname = elem.getProperty("fname"); //first name
entryBtns[j-currentRecord] = new Button(fname+" " +lname,
5, (j-currentRecord)*20+15);
entryBtns[j-currentRecord].paint();}
Here we make use of NanoXML to parse the first and last name in the record.
The first and last name become the button text. nanoxml.kXMLElement is the CLDC/KVM
port of nanoxml.XMLElement and has the same public methods. Records are
written by class AddRecord (see Generating XML from the Form, page 628).
A typical document instance might look like this:
<contact fname="Frantic" lname="Neumann"
address="123 Fake
Street" city="Denver"
state="Colorado" zip="80202" country="USA"
phone="303-303-3000"/>
Since we're through with the database, we can close it. The only thing that's
left is to draw Next and Prev buttons.
db.close(); //close the database
We don't want to draw the Next button if there aren't any more records to be
displayed, so this logic accounts for that:
//draw the "Next " button, if appropriate
if (currentRecord + numEntryBtns + 1 <=
numRecords){
nextBtn = new Button("Next",
100, 140);
nextBtn.paint();
}
else
nextBtn = null;
Likewise, we don't want to draw the Prev button if we're displaying the first
page of records.
//draw the "Previous" button, if
appropriate
if (currentRecord >= ENTRIES_PER_PAGE)
{
prevBtn = new Button("Prev",
65, 140);
prevBtn.paint();
}
else
prevBtn = null;
Beaming the XML Document
When the user clicks one of the entryBtns, BeamContact receives a penDown()
event. It's in this event handler that we determine which, if any, of the
entryBtns were pressed. If one was pressed, BeamContact.beamRecord() is called
with the record number to beam.
Infrared beaming itself is quite simple. The XML document must be beamed as a
byte array, since com.sun.kjava.Spotlet requires all beamed content to be an
array of bytes. com.sun.kjava.Spotlet provides a method called beamSend() that
we will use for the physical data transfer:
public static boolean beamSend(byte[] data)
So let's look at our last method for this class, beamRecord(). It is called
by penDown() when a button other than Quit, Next, or Prev is clicked. recordNum
is the record in the contact book that the user wants to beam.
private void beamRecord(int recordNum) {
Database db = new Database(ContactBook.dbType,
ContactBook.creatorId, Database.READONLY);
if (db == null)
System.err.println("Error opening contact
book");
else {
//read and beam it!
if (beamSend(db.getRecord(recordNum)))
new Dialog(this, "Success", "Beamed
contact!",
"Ok").showDialog();
else
new Dialog(this, "Failure", "Could not beam
contact!",
"Ok").showDialog();
}
com.sun.kjava.Database.getRecord() makes a native Palm OS call and returns a
byte array with the raw content of the desired record:
public native byte[] getRecord(int recordNumber);
ReceiveContact
public class ReceiveContact
extends com.sun.kjava.Spotlet
ReceiveContact is the core class for receiving a contact from the
BeamContact application. It must extend com.sun.kjava.Spotlet to fit into the
package com.sun.kjava application paradigm.
Spotlet is conceptually similar to java.applet.Applet in that it is a class
for handling event callbacks. Overriding one of the event handlers, such as
penMove(), keyDown(), or keyUp(), will enable the extended class (ReceiveContact,
in this case) to receive notification of these events.
Upon running ReceiveContact, the application blocks until it receives beamed
data. Although this is hardly adequate for a commercial environment, the
application nevertheless demonstrates the technologies in this chapter quite
well.
ReceiveContact blocks until it receives beamed data. If the data received is
an XML document of the document class we've defined implicitly, the user is
prompted to import the data into the contact book. If answered affirmatively, a
new record is created in the database with that data. If the received data isn't
XML or is not of the expected document class, it is ignored and ReceiveContact
continues to block.
Here's a screenshot of the application before it receives a contact so you
get a feel for how it works. There is only one other screen besides this one -
the screen that prompts the user to import the contact.

We'll examine two parts of classReceiveContact:
- Receiving beamed data and parsing it with NanoXML
- Writing the beamed data to the database
Receiving the XML Document
The process to receive beamed infrared data is quite simple.
com.sun.kjava.Spotlet provides an event handler called beamReceive() that we
override:
public static void beamReceive(byte[] data)
So let's look at our ReceiveContact.beamReceive(). It is called when the
class Spotlet dispatches infrared data to our handler.
public void beamReceive(byte[] buf) {
//parse the received xml using NanoXML
contact = new String(buf);
kXMLElement elem = new kXMLElement();
try {
elem.parseString(contact);
}
catch (XMLParseException e) {
//can't parse received data--the sender probably
//wasn't BeamContact!! ignore it and return.
paint();
return;
}
All we're doing here is converting the byte array to a java.lang.String and
passing it to NanoXML to parse. nanoxml.kXMLElement is the CLDC/KVM port of
nanoxml.XMLElement and has the same public methods. See NanoXML (page
577) for more information. A typical document instance, after converted from a
byte array to a String, might look like this:
<contact fname="Frantic" lname="Neumann"
address="123 Fake
Street" city="Denver"
state="Colorado" zip="80202" country="USA"
phone="303-303-3000"/>
The most common cause of an exception being thrown here is receiving data
that isn't XML. The Palm OS routes received infrared data to the same
application on the receiving unit that sent the data from the sending unit. This
sandbox restriction pretty much guarantees that Palm's Memo Pad won't beam the
contact book data, but it is restrictive; in our case, the sending and receiving
applications, from the Palm OS's perspective, in the KVM. However, the Palm OS
has no idea what application is running inside the KVM. It is therefore possible
that another Java application running inside the KVM could beam data to
ReceiveContact. If this occurs, beamReceive() calls paint(), which clears the
screen and redraws the message, "Waiting for contact information…",
and then returns.
Here's the rest of the beamReceive():
String fname = elem.getProperty("fname"), lname =
elem.getProperty("lname");
if (fname != null && lname != null) {
g.clearScreen();
g.drawString("Contact info was received for:",
0, 20);
g.drawString(fname + " " + lname, 0, 40);
g.drawString("Import into contact book?", 0,
60);
yesBtn = new Button("Yes", 40, 80);
noBtn = new Button("No", 60, 80);
yesBtn.paint();
noBtn.paint();
}
else paint();
}
kXMLElement.getProperty() returns null if a property doesn't exist in the
element in which we are manipulating (in this case, the root element). If either
the fname or lname properties don't exist, we ignore the received data, paint()
the screen with the message "Waiting for contactinformation…"
and then return. We could do further document checking by ensuring that the root
element is named contact.
Writing the XML Document
The final step is to ask handle the yes/no button click. If yes is pressed,
we must open the database and write the contact (received XML) as a new record.
If no is pressed, we repaint the screen with the "Waiting for contact
information…" message. As we've already seen, button clicks are
handled by overriding the com.sun.kjava.Spotlet.penDown() event handler. If the
exit button has been pressed, we register our "owner" - the
ContactBook object - as the current Spotlet event listener (remember, there is
only one Spotlet listening for events at a time), and tell ContactBook to paint
itself. This effectively hands control back to the main menu. The owner is set
in the ReceiveContact constructor:
import com.sun.kjava.*;
import nanoxml.kXMLElement;
import nanoxml.XMLParseException;
public class ReceiveContact extends Spotlet {
private Button exitBtn = new Button("Quit", 135, 140),
yesBtn, noBtn;
private Graphics g = Graphics.getGraphics();
private ContactBook owner;
private String contact;
public ReceiveContact(ContactBook owner) {
register(NO_EVENT_OPTIONS);
this.owner = owner;
paint(); //paint the "Waiting for..." message
}
public void penDown(int x, int y) {
//did the user press the exit btn?
if (exitBtn.pressed(x,y)) {
owner.register(NO_EVENT_OPTIONS);
owner.paint();
}
//did the user press the yes btn, telling us to
//write the received contact into the database?
else if (yesBtn.pressed(x,y)) {
Database db = new Database(ContactBook.dbType,
ContactBook.creatorId,
Database.WRITEONLY);
if (!db.isOpen()){
//the database doesn't exist yet.
create it.
Database.create(0,
ContactBook.dbName,
ContactBook.creatorId,
ContactBook.dbType, false);
db = new
Database(ContactBook.dbType,
ContactBook.creatorId,
Database.WRITEONLY);
}
db.addRecord(contact.getBytes());
db.close();
paint(); //repaint the "Waiting
for..." message
}
else if (noBtn.pressed(x,y))
paint();
}
public void beamReceive(byte[] buf) {
//parse the received xml using NanoXML
contact = new String(buf);
kXMLElement elem = new kXMLElement();
try {
elem.parseString(contact);
}
catch (XMLParseException e) {
//can't parse received data--the sender
probably
//wasn't BeamContact!! ignore it and return.
paint();
return;
}
String fname = elem.getProperty("fname"),
lname = elem.getProperty("lname");
if (fname != null && lname != null)
{
g.clearScreen();
g.drawString("Contact info was received
for:", 0, 20);
g.drawString(fname + " " + lname, 0,
40);
g.drawString("Import into contact
book?", 0, 60);
yesBtn = new Button("Yes", 40, 80);
noBtn = new Button("No", 60, 80);
yesBtn.paint();
noBtn.paint();
}
else
paint();
}
public void paint() {
g.clearScreen();
yesBtn = noBtn = null;
exitBtn.paint();
g.drawString("Waiting for contact
information...", 10,
70);
}
}
In this chapter, we examined Java and XML on lightweight clients, also known
as information appliances. We talked about where XML on lightweight clients
stands today, and why we need XML on lightweights.
Then we discussed the architecture of Java 2 Micro Edition: the application
layer, the device profile layer, the configuration layer, and finally the
operating system layer. Focusing more on the Connected Limited Device
Configuration, as it is more "lightweight" than the Connected Device
Configuration, we discussed the Java Kilobyte Virtual Machine, kjava, and kAWT.
Next we reviewed parsers for lightweights. Pull parsers such as kXML and XPP
hold a lot of potential for information appliances, but since there is no
industry-accepted standard interface, we focused on DOM-style and SAX parsers.
We covered two very different versions of NanoXML in-depth, as well as NanoXML's
SAX interface. We also discussed MinML with its SAX interface.
XSLT Compiler, by Sun Microsystems, was covered in detail. We wrote an XSL
stylesheet, compiled a translet, and executed a small TroubleTicket application
that used the translet with an input XML document. XSLTC's speedy execution and
small size make it ideal for performing XSL transformations on information
appliances.
SOAP on information appliances is very appealing, but library support is
limited currently. It might be best to write your own library if you can't wait
until kSOAP or another small library matures.
Finally, we built a peer-to-peer contact book application for the Palm OS
using NanoXML, Java KVM, the Palm OS Emulator, and some GUI classes made
available by Sun. This infrared beaming application prompted the user with a
list of contact book entries to beam, while a receiving application waited for
an XML document to insert into its contact book database.
| Page 3 of 3
|
|
|