Building Wireless Web Clients, Part 2
Pages: 1, 2, 3
What is Stored in the Bookstore?
In the first part of this article, we used HTTP to get the details for a book
and convert them into an instance of the BookInfo class. This class, which we didn't show in the first part of the article, has the following instance variables:
public class BookInfo {
int id; // Used when persisting
String isbn; // The book ISBN
String title; // The book title
int reviews; // Number of reviews
int ranking; // Current ranking
int lastReviews; // Last review count
int lastRanking; // Last ranking
}
The isbn variable is the book ISBN obtained from the user.
The values of all of the other variable, with the exception of id (which will be described
below), are extracted from the HTML. The reviews and
ranking fields hold the current values, while lastReviews
and lastRanking are the values that were obtained on the previous query.
Each time the book's Web page is fetched from the server, the value of reviews
and ranking are copied to lastReviews and lastRanking,
respectively, and the values extracted from the Web page are written to reviews
and ranking.
The BookInfo class is designed to hold all of the information relating to
a book in the BookStore. The intent is that each BookInfo instance will be mapped to a record in the underlying RecordStore. In terms
of the RecordStore APIs, a record is simply a contiguous sequence of bytes,
the meaning of which is opaque to the platform and to the classes in the
javax.microedition.rms package. The RecordStore class provides
several methods that operate on records:
public int addRecord(byte[] data, int offset, int length)- Creates a new record initialized with the specified portion of an array of bytes, returning
a unique integer identifier known as
recordId. public void setRecord(int recordId, byte[] data, int offset, int length)- Replaces the content of the record identified by
recordIdwith the given portion of an array of bytes. This may cause the record to grow or to become smaller. public byte[] getRecord(int recordId)- Returns the content of the record with a given
recordId. public int getRecord(int recordId, byte[] buffer, int offset)- Reads the given record into the supplied array, starting at the given offset, and
returns the number of bytes that were read.
public void deleteRecord(int recordId)- Deletes the record with the given
recordId.
In order to add a record for a new book or replace the data for an existing book,
we need to create an array of bytes that represents its BookInfo
instance and use either the addRecord() or setRecord() methods
to write the bytes to the RecordStore. If you were using J2SE, you might
take advantage of object serialization to create a
serialized version of each instance of BookInfo for storage, or use the Java Beans
persistance mechanism introduced in Java 2 version 1.4 to flatten the object into an
XML representation. Unfortunately, neither of these is available to MIDlets. The most
convenient way for a MIDlet to convert a class instance into a record is to use a
DataOutputStream in conjunction with a ByteArrayOutputStream.
Here, for example, is how the BookStore class converts a
BookInfo object into a byte array:
// Writes a record into a byte array.
private byte[] toByteArray(BookInfo bookInfo) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream os = new DataOutputStream(baos);
os.writeUTF(bookInfo.isbn);
os.writeUTF(bookInfo.title == null ? "" : bookInfo.title);
os.writeInt(bookInfo.ranking);
os.writeInt(bookInfo.reviews);
os.writeInt(bookInfo.lastRanking);
os.writeInt(bookInfo.lastReviews);
return baos.toByteArray();
}
The function of the ByteArrayOutputStream is to write everything that it
receives into a buffer that can be extracted in the form of a byte array, while the
DataOutputStream provides convenience methods that write Java primitives,
types, and strings in a platform-independent way to an output stream.
You'll notice that the toByteArray method does not write out the id
field from the BookInfo class. This is because each record is implicitly
associated with its own recordId and therefore it does not need to be
stored in the record content. The recordId is a unique positive
integer value assigned when the record is created. Each time a record is written,
it is assigned the next recordId, starting from 1. The software guarantees that
recordIds are not reused. In particular, if you delete a record, its
recordId is not reassigned at any time in the future. The
getNextRecordID() method can be used to find out the recordId
for the next record to be written to the RecordStore.
Reading the content of a record and converting it to a BookInfo object
is a simple matter of reversing the steps shown above, using a
ByteArrayInputStream and a DataInputStream to map the
byte array returned from the RecordStore method getRecord()
to the original Java types:
public BookInfo getBookInfo(int id) throws RecordStoreException,
IOException {
byte[] bytes = store.getRecord(id);
DataInputStream is = new DataInputStream(
new ByteArrayInputStream(bytes));
String isbn = is.readUTF();
BookInfo info = new BookInfo(isbn);
info.id = id;
info.title = is.readUTF();
info.ranking = is.readInt();
info.reviews = is.readInt();
info.lastRanking = is.readInt();
info.lastReviews = is.readInt();
return info;
}
This method assumes that we already have the recordId for the book whose
details we require. So how do we get this value?