ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Building Wireless Web Clients, Part 2

by Kim Topley

The first part of this article showed how to use the HTTP support in a MIDP device to connect to Amazon's online bookstore and fetch details for a book, given its ISBN. The choice of ISBN as the key was based on the fact that the Amazon Web server provides a query that returns the details for a book given its ISBN, whereas searching by other means (such as author name or book title) sometimes results in an intermediate page being delivered, buried within which is a link to the page that the application needs.

From the user's point of view, however, ISBN is about the worst choice that could have been made. How many ISBNs do you carry around in your head? Even the most zealous of authors, I'm prepared to bet, don't memorize the ISBNs of all (or even any) of their books. To make the user's life easier, the second part of this article creates a local bookstore for the user. The local bookstore retains the details of all books previously obtained with the ISBN application, so the user can locally look up books by title. The application then gets the ISBN from the stored book details and uses the ISBN query to download the latest information from Amazon. In addition, the bookstore retains the sales ranking and the number of reviews, so that they can be presented from the local cache without having to make a network connection. You can also compare the old (local) figures with the latest data fetched from Amazon.

The Bookstore Web Client

The bookstore is implemented as a new MIDlet, called PersistentRankingMIDlet, which is in the same MIDlet suite as the MIDlet shown in the first part of this article. When this MIDlet executes for the first time, it prompts you to enter an ISBN and then fetches its title, sales ranking, and number of reviews, just as the first MIDlet did. It then stores these details on the device so that the next time you start the MIDlet, you will see a list of the books that you have already made queries for, as shown in the leftmost screenshot of Figure 1. This list is presented in alphabetical order, sorted by the book's title.

Screen shot.
Figure 1. List of books previously queried.

When you select a book from the list, the most recently obtained details are displayed, along with a button called Menu that allows you to access a menu of possible operations, shown in Figure 2.

Screen shot.
Figure 2. Menu options.

In this menu, the New operation lets you enter a new ISBN, and is therefore equivalent to the functionality provided by the RankingMIDlet developed in the first part of this article. The Delete option removes a book from the bookstore. Finally, the Details menu item causes the application to connect to Amazon.com, refresh the book details, and present a screen showing the difference between the new and old sales rankings and number of reviews. Figure 3 shows that the book has a ranking of 7,303, up 528 from the last time the device queried for information on the book.

Screen shot.
Figure 3. Display of current data from Amazon.com.

Storing Book Details

The extra facilities provided by this MIDlet require the ability to store book details on the MIDP device. All MIDP devices will provide some kind of long-term storage that is guaranteed to be preserved, at least while the device has some kind of power applied to it, which in the worst case means as long as the battery powering the device is not allowed to become completely discharged. Different device types provide different types of storage, so the MIDP profile defines a device-independent programming interface that allows a MIDlet to use whatever storage is available without needing to be aware of how it is actually implemented on the device. The API for this record management system is provided in the javax.microedition.rms package, a complete description of which, along with annotated reference information, can be found in O'Reilly's J2ME in a Nutshell.

J2ME in a Nutshell

Related Reading

J2ME in a Nutshell
By Kim Topley

What is the Bookstore?

The MIDP record-management APIs are based around the RecordStore class, which represents a collection of related records. Each RecordStore on a device is created and managed independently and belongs exclusively to the MIDlet suite containing the MIDlet that created it. All MIDlets in this suite can read and write records in any of their shared RecordStores and can delete the individual RecordStores if required. On the other hand, for security reasons, MIDlets cannot access or even find out about the existence of RecordStores belonging to other MIDlet suites. The tight binding between a MIDlet suite and its RecordStore is also apparent in a couple of other ways:

  • A RecordStore has a name composed of up to 32 Unicode characters. This name is case-sensitive and must be unique within the MIDlet suite. However, the names used by one suite can overlap those used by a different suite, because the MIDlet suite is implicitly part of the platform-dependent key used to identify the underlying resources in which the RecordStore is implemented.

  • When a MIDlet suite is removed, any RecordStores that any of its MIDlets have created are automatically deleted at the same time. Since MIDlets can only be installed or removed as suites, the issue of what should happen to a RecordStore created and used by one MIDlet in a suite does not arise.

For the purposes of the bookstore client, we create a class called BookStore that provides a higher-level interface, which allows the application to work with the data for an individual book rather than the records that the RecordStore class deals with. As you'll see later, those records are a very primitive concept. The BookStore class maps directly to a RecordStore called, appropriately enough, BookStore, which is automatically created or opened as required.

The static openRecordStore() method opens a RecordStore given its name:

public static RecordStore openRecordStore(String name, boolean create)

If a RecordStore with the given name exists, it is opened, and an appropriate RecordStore object is returned. If it does not exist and the create argument is true, then an empty store is created. If create is false, this method throws a RecordStoreNotFoundException, one of several exceptions derived from the base class RecordStoreException that can be thrown by methods in the classes of the javax.microedition.rms.

The BookStore class opens or creates its associated RecordStore when its constructor is executed:

public class BookStore implements RecordComparator, RecordFilter {

   // The name of the record store used to hold books
   private static final String STORE_NAME = "BookStore";

   // The record store itself
   private RecordStore store;

   // Creates a bookstore and opens it
   public BookStore() {
  try {
 store = RecordStore.openRecordStore(STORE_NAME, true);
  } catch (RecordStoreException ex) {
 // Error handling not shown

A RecordStore can be opened several times by a single MIDlet and can also be open for access simultaneously by more than one MIDlet (in the same suite, of course). The RecordStore implementation keeps track of the number of times that a given store has been opened. This count is decremented when the RecordStore's closeRecordStore() method is called; the underlying storage is closed (if this concept exists) only when the RecordStore has been closed as many times as it was opened. This means that each invocation of openRecordStore() must be balanced by a corresponding call to closeRecordStore().

The RecordStore class has several global operations that operate at the record store level, including:

public static String[] listRecordStores()
Returns an array containing the names of the available RecordStores. For security reasons, only the names of those RecordStores created by the MIDlet suite containing the calling MIDlet appear in this array.

public static void deleteRecordStore(String name)
Deletes the record store with the given name. The name is, of course, interpreted relative to the record stores owned by the calling MIDlet's suite, so that storage belonging to other suites cannot be deleted. A RecordStore cannot be deleted while it is open.

public void addRecordListener(RecordListener l)
Registers a listener to be notified when the content of the RecordStore changes. The RecordListener interface defines methods that are called when records in the RecordStore are added, removed, or updated.

public void removeRecordListener(RecordListener l)
Removes a listener previously registered using addRecordListener().

public int getSize()
Returns the number of bytes of storage occupied by the RecordStore, which includes any private storage management information required by the implementation.

public int getSizeAvailable()
Returns the number of bytes by which the RecordStore can grow. Since the implementation requires some space for private management information, the number of bytes available for actual MIDlet data will usually be less than the value returned by this method.

public long getLastModified()
Returns the time at which the RecordStore was last changed, in the same form as the value returned by the System method currentTimeMillis().

public int getVersion()
Gets the version number of the RecordStore. This value is changed whenever the content of the RecordStore is changed in any way. Checking this value is a quick way to determine whether the store has been modified.

public int getNumRecords()
Gets the number of records in the RecordStore.

The BookStore class provides a simpler interface that allows applications to work in terms of the BookStore itself, instead of dealing with the underlying RecordStore. Here, for example, are the methods that get the number of books in the BookStore and allow the BookStore to be closed. As you can see, they both delegate directly to the corresponding RecordStore methods:

// Closes the bookstore
public void close() throws RecordStoreException {
    if (store != null) {

// Gets the number of books in the bookstore
public int getBookCount() throws RecordStoreException {
   if (store != null) {
        return store.getNumRecords();
    return 0;

Pages: 1, 2, 3

Next Pagearrow