Sun Java Solaris Communities My SDN Account Join SDN
 
Article

PeekAndPick 2.0 Design and Implementation

 



The PeekAndPick application allows you to browse news headlines and have story links emailed to your desktop. This article describes the client application from a developer's perspective. It covers relevant background information, provides a high-level view of the application, and describes components that you can use in your own applications.

PeekAndPick is an end-to-end Java application, comprising both a MIDlet client and a simple servlet. This article presents the client side of PeekAndPick and discusses the protocol used between client and server.

You may wish to download the source code so you can examine the files as you read the article. I'll also assume you are already familiar with PeekAndPick from a user's perspective; if you need to find out more, read the User's Guide.

The PeekAndPick source code is available as a zipped archive. There are two ways to build the application: can use the J2ME Wireless Toolkit graphic interface (KToolbar), or use Ant on the command line. Both methods rely on tools in the J2ME Wireless Toolkit, which in turn relies on an installation of Java 2, Standard Edition (J2SE) SDK. See Wireless Development Tutorial Part I for details about installing the wireless toolkit.

Building PeekAndPick

To build PeekAndPick using KToolbar, follow these steps:

  1. Unzip the source archive into the toolkit's apps directory. Doing so will create a new project directory, PeekAndPick20, and upack files into bin, build, lib, res, and src subdirectories.
  2. Run KToolbar.
  3. Click on Open Project... and select PeekAndPick20 from the list. Click on Open Project.
  4. Click on Build. The project should build without errors. You can now run PeekAndPick by clicking on Run.

To build PeekAndPick using Ant, follow these steps:

  1. Unzip the PeekAndPick source archive in the directory of your choice.
  2. Edit the build/build.xml script. You may need to adjust the value of the midp property to point to your J2ME Wireless Toolkit installation.
  3. Open a command window and change to the build directory of the source code. Optionally, type ant tools to add a task to Ant. (You need to add the task only once.)
  4. Type ant. The project should build without errors. If you wish to use the Ant task from the last step, type ant jad instead. This is equivalent to ant but also adjusts the MIDlet-Jar-Size in the descriptor.
  5. To run the project in the emulator, type ant run.

If you wish to use the ProGuard obfuscator, you need to download and install it separately. Make sure the proguard property in build.xml points to the ProGuard classes, and change the depends attribute of the preverify target from obfuscate_null to obfuscate_proguard.

Similarly, if you wish to use the Retroguard obfuscator, make sure the retroguard and midp_lib_empty properties are properly defined. Change the depends attribute of the preverify target from obfuscate_null to obfuscate_retroguard.

(For more information on Retroguard, see Obfuscating a J2ME MIDlet suite.)

The build.xml script provides several other useful targets:

  • The clean target removes subdirectories of the build directory that are created during the build process.
  • The reallyclean target does what clean does and also removes the compiled project and other files.
  • The docs target uses javadoc to generate API documentation for the PeekAndPick classes. You can also browse this documentation online.

RSS Crash Course

To understand how PeekAndPick works, you'll need to understand something about RSS feeds. RSS (for Rich Site Summary) is an XML application. An RSS file (a feed) contains a description of the contents of a Web site. The Sun Wireless Developer site has an RSS feed located at http://wireless.java.sun.com/xml/wd.rss. (Depending on your browser and configuration, you may or may not be able to view this file easily.) This site's RSS feed lists the most recently added items on the site. For each item, the RSS feed contains a title, a URL, and a short description.

The power of this simple XML file is that any client application that recognizes RSS can now read a summary of this site. RSS clients are available for many different platforms. Such clients can easily pull news summaries from various feeds around the Internet and present them in a variety of ways to the user.

The Meerkat service, hosted by the O'Reilly Network, takes the idea of RSS one step further. Meerkat aggregates items from many different feeds into a single one, also in RSS format. Meerkat has a documented API which makes it an extremely powerful and flexible tool for extending other applications. You can ask for specialized results, for example retrieval of items added in the last ten days that come from feeds concerning wireless technology. You can perform keyword searches. You can retrieve results in a variety of formats, not just RSS.

RSS is well documented and widely supported. For more information, try these resources:

PeekAndPick Architecture

PeekAndPick is an RSS client application written in Java for the Mobile Information Device Profile (MIDP). Its architecture looks like this:

PeekAndPick architecture
PeekAndPick architecture

PeekAndPick makes two different kinds of network connections. First, it connects to RSS feeds so it can download article information and display it to the user. Second, the PeekAndPick MIDlet connects to the PeekAndPick server to send email.

PeekAndPick can connect to any RSS feed on the Internet. The current version includes several feeds that have been hardcoded in the application, and the FeedEditor MIDlet can be used to edit the list. PeekAndPick downloads and parses a feed, and displays a list of the item titles. The user can browse through the titles, view details behind any of them, and mark items that look interesting.

The user can then direct PeekAndPick to include links to all marked items in a message to be sent to the user's email address. The PeekAndPick MIDlet makes an HTTP connection to the PeekAndPick servlet running on a Web server. The servlet connects to an email server and sends the message.

Internal Architecture

The PeekAndPick client consists of about 20 classes and interfaces:

PeekAndPick UML class diagram excerpt
PeekAndPick UML class diagram excerpt
(Click image to view classes.)

The application's center is PeekAndPick, a MIDlet. PeekAndPick controls all the other classes in the application. It also responds to the user's input from several different screens and figures out what to do next. A second MIDlet, FeedEditor enables the user to edit the list of feeds.

PeekAndPick calls on classes and interfaces in four packages. The contents of these packages are described below. Later, I'll go into more detail, especially about code that you might use in your own applications.

  • The default package contains classes that are specific to the PeekAndPick application. Instances of Feed represent the RSS feeds used by PeekAndPick. FeedStore provides persistent storage for Feeds, and Parameter also supports Feed.
  • The rss package is dedicated to parsing RSS documents. Parser is an abstract class that defines basic behavior. Concrete subclasses must be able to parse an RSS document represented by an InputStream. While they parse, they should spit out events to registered ParserListeners. One concrete subclass, kXML12Parser, is provided. It uses the kXML 1.2 parser.
  • The util package contains a generalized Worker class that is used for threading support. PeekAndPick's performs its network access in a separate thread to avoid locking up the user interface. The Worker class maintains a queue of tasks that will be run in a single separate worker thread. The tasks are represented by instances of the WorkerTask interface, which contains only a run() method, just as Runnable does. When tasks are complete, or when they throw exceptions, a registered WorkerListener is notified.

    The util package also contains a generalized Preferences class and an associated GUI; PeekAndPick uses Preferences to store the user's email address, the address of the PeekAndPick server, and the user's chosen font size.
  • Finally, the display package contains generalized UI components. ListCanvas, ItemCanvas, and DisplayItem handle display of the list of titles and of the details of any article selected by the user. SplashScreen displays the application's starting image. The ProgressDisplay interface specifies a simple interface for an object that can display a progress indicator to the user. In PeekAndPick 2.0, ListCanvas implements ProgressDisplay to display a simple animated progress bar while the application is making network connections. Code in Worker sets the progress indicator running when a task is pending. Worker clears the progress indicator when it has no more tasks to run.

Using a Worker Thread

The util.Worker class provides a general approach to the problem of running tasks in a separate thread. Worker maintains a queue of WorkerTasks, which are run in the order they're added to the queue. When one task finishes, Worker starts the next. If the queue is empty, Worker patiently waits for a new task to be added.

An application is notified about task completion or exceptions if it implements the WorkerListener interface.

PeekAndPick uses Worker to execute two tasks. The first, ParseTask, retrieves and parses RSS files. The second, MailTask, connects to the PeekAndPick server to request that it send email.

Using the Worker class is straightforward. PeekAndPick begins by creating a Worker and registering itself as the listener object. This code is in PeekAndPick's initialize() method:

// mWorker is a member variable.
mWorker = new Worker();
mWorker.setWorkerListener(this);

It's easy to use the worker thread in an application of your own; just create an instance of WorkerTask and pass it to the Worker. For example, the following code from PeekAndPick's commandAction() shows how to create a task for parsing an RSS feed:

// mParser is a member variable, a Parser instance.
// feed contains the currently selected Feed.
ParseTask task = new ParseTask(mParser, feed, null);
mWorker.queue(task);

Creating a task is even easier. All you do is implement the WorkerTask interface and put the code for the task inside the run() method. See ParseTask and MailTask for examples.

Why doesn't Worker just use java.lang.Runnable instead of the new WorkerTask interface? Worker needs to catch exceptions thrown by tasks. The run() method of Runnable throws no exceptions, so it doesn't provide the functionality that Worker needs.

You can read more about the general problem of running network activity in its own thread in Networking, User Experience, and Threads.

Persistent Preferences

One requirement commonly faced by network-client MIDlets is the need to store user preferences. PeekAndPick's Preferences class provides a generalized class for maintaining and storing user preferences.

User preferences are stored as key-and-value pairs, where each key and each value is a String--much as in the java.util.Properties class in J2SE. You can add or replace a value using put(), or pass a key to get() to retrieve a value. Preferences also includes a putIfNull() method you can use to initialize missing values. This excerpt from PeekAndPick's initialize() method shows how the Preferences object is created. If the method can't load the preferences (because the application is being run for the first time), it initializes values from system properties.

// mPreferences is a member variable.
mPreferences = new Preferences("PeekAndPick.preferences");
String email = getAppProperty("PeekAndPick.preferences.email");
String server = getAppProperty("PeekAndPick.server.URL");
mPreferences.putIfNull("Email address", email);
mPreferences.putIfNull("PeekAndPick Server", server);

Preferences store themselves using MIDP's RecordStore. When you create a Preferences object you pass the name of a record store to the constructor. Preferences attempts to load itself from that record store. To save a Preferences object after making changes, call the save() method.

Editing Preferences

The UI component PreferencesEditor provides controls to allow users to modify the values of a Preferences object. It is a subclass of javax.microedition.lcdui.Form. To create a PreferencesEditor, pass a Preferences object and a String array of non-editable keys to the constructor. The PreferencesEditor will not display user interface controls for non-editable values.

PeekAndPick maintains an email address, a server URL, and a font size in its Preferences object. The use enters the value of the email address (key is "Email address") and the font size (key is "Font size"), but the server URL (key is "PeekAndPick Server") should not be editable--indeed, the user should never see it. PeekAndPick's getPreferencesEditor() method creates the editor this way:

// mPreferences is a member variable.
String[] noShows = { "PeekAndPick Server" };
mPreferencesEditor = new PreferencesEditor(mPreferences, noShows);

This editor form is shown when you choose the Prefs command. PreferencesEditor creates one TextField for each editable value in the Preferences object. For PeekAndPick, the "Email address" and "Font size" are the only editable values, so the PreferencesEditor contains a TextField and a ChoiceGroup:

PreferencesEditor with two fields
PreferencesEditor with two fields

You can reset an existing PreferencesEditor to the new values of a Preferences object by calling its initialize() method. To retrieve the edited values from the form, call the extract() method. This method returns a Hashtable with keys and values for each of the fields in the form. Note that the returned Hashtable does not contain any of the non-editable key-and-value pairs. You can take the values returned by extract() and pass them to the set() method in Preferences, which adds to or overwrites the existing values.

Pluggable Parsing

Parsing RSS is one of the simpler tasks in PeekAndPick. When I designed the parsing classes I made it easy to swap in different XML parsers with only minimal code changes in the rest of the application. PeekAndPick parses the XML using a concrete subclass of the abstract rss.Parser.

Parser is relatively simple. It contains the plumbing for maintaining a list of listeners, and protected methods for firing various events to those listeners. Its one abstract method is parse(). This method is expected to parse the given RSS byte stream and notify the listeners appropriately.

rss.kXML12Parser is a concrete subclass that uses the kXML 1.2 parser. It's relatively simple; it just walks through the document, searching for item tags and pulling out the information it needs.

Fortunately for code maintenance, PeekAndPick doesn't know much about kXML12Parser. The only place the name even comes up is at creation time, in PeekAndPick's initialize() method. PeekAndPick creates the parser and adds itself as an event listener.

// mParser is a member variable of type rss.Parser.
mParser = new kXML12Parser();
mParser.addParserListener(this);

Thereafter, PeekAndPick treats the object as a Parser instance, which means it would be easy to drop in a different implementation. Suppose kXML 2.0 is released. I could write a kXML20Parser class that uses the new version and change the initialization in PeekAndPick to use the new class. Everything else would stay the same.

Instead of creating the parser subclass explicitly, I could even use Class.forName() and use a system property to specify the name of the implementation class, but I feel going that far would be overkill.

For more information about using XML parsers in MIDP and the specific task of parsing RSS, read Parsing XML in J2ME.

Lists and Details

The class that displays PeekAndPick's list of article titles is display.ListCanvas. It is not specific to PeekAndPick or RSS. It can display any list of items, where each item consists of a name, a description, and another piece of text that is not shown to the user. In addition, ListCanvas allows the user to check or uncheck each item in the list.

ListCanvas in action
ListCanvas in action

You can add items to ListCanvas by calling addItem(). Item information can be retrieved using the getShortText(), getLongText(), and getExtra() methods, each of which takes an index to the item. Alternately, you can retrieve a complete item with getItem(), which returns an instance of DisplayItem.

ListCanvas is a subclass of Canvas and handles key events. Specifically, a press on the right-arrow key of the device will cause ListCanvas to display details for a single item, using ItemCanvas. You can also request this behavior by calling the details() method. Similarly, ItemCanvas will respond to a left-arrow key press by returning to the original ListCanvas.

The setMarkingAllowed() method in ListCanvas controls whether or not marking (checking) is supported. If it is, each time the select key is pressed, ListCanvas toggles an item's check mark. You can find out whether an item is marked by calling the isMarked() method.

Functionally, this class is similar to the MULTIPLE type of javax.microedition.lcdui.List. I created ListCanvas to allow more sophisticated navigation instead of relying on Commands exclusively. Note that, like List, ListCanvas adjusts itself to the available screen size, so it looks right on a mobile phone as well as a PDA.

A Simple Splash Screen

The display.SplashScreen class is a simple implementation of a splash screen that displays an image. When you instantiate it, pass the constructor the name of the image file (a Display object) and the next screen that should appear when the splash screen goes away.

The splash screen dismisses itself after a hard-coded time-out (three seconds). It will also dismiss itself in response to a key press or a pointer press.

Client-Server Protocol

You now understand how PeekAndPick parses RSS feeds. In this section I'll describe how the PeekAndPick client MIDlet talks to the PeekAndPick server, and requests that it send email. The protocol is very simple. The client MIDlet sends an HTTP POST request whose body is of this form:

log line
destination line
message line 1
message line 2
...
message line n

The first line contains information that the servlet can write into the server log. In PeekAndPick's current implementation this line is blank. The second line is the destination email address, the one the user entered into the preferences screen. The remainder of the request body is the message itself. The MIDlet creates this message from the items in the title list that the user has marked.

On the server side, the servlet simply parses out the information from the HTTP POST request and uses the Java Mail API to create a message and send it.

Summary

In this article you learned about the design and implementation of the PeekAndPick client MIDlet. You encountered many classes you can use in your own applications. PeekAndPick is a useful application and a demonstration of MIDP client programming. It should give you a taste of how an end-to-end Java solution works. Please feel free to use pieces of PeekAndPick in your own applications, subject to the license.



Back To Top

Oracle is reviewing the Sun product roadmap and will provide guidance to customers in accordance with Oracle's standard product communication policies. Any resulting features and timing of release of such features as determined by Oracle's review of roadmaps, are at the sole discretion of Oracle. All product roadmap information, whether communicated by Sun Microsystems or by Oracle, does not represent a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. It is intended for information purposes only, and may not be incorporated into any contract.