This paper is about wireless Java programming with the Java 2 Platform, Micro Edition (J2ME). Sun Microsystems, Inc. introduced J2ME at the JavaOne conference in June 1999 as the younger sibling of both the Java 2 Standard Edition (J2SE) and the Java 2 Enterprise Edition (J2EE). At the time, distributed programming was taking the Java developer community by storm, so most of the participants at the show were more interested in what J2EE had to offer. However, over the next two years, developers also realized that there was tremendous value in having small components running Java. Two years later, at the 2001 JavaOne conference, Sun devoted an entire track for individuals seeking to master the once arcane J2ME. Luckily, you don't need to attend JavaOne to learn about J2ME. Instead, this paper will help you through the myriad details of understanding J2ME architecture and programming J2ME applications.
In this paper, we will present an overview of J2ME's primary components, including virtual machines, configurations, and profiles. We'll then present a few short examples of J2ME-enabled applications to whet your appetite and to show you how easy it is to get started with J2ME.
J2ME is a version of Sun Microsystems' Java that is aimed at the consumer and embedded devices market, which includes electronic commodities such as cellular telephones, pagers, Personal Digital Assistants (PDAs), set-top boxes, and other small devices. Since its release, over 600 companies have joined the development effort, including large corporations such as Palm, Nokia, Motorola, and RIM. However, the direction that J2ME travels is not shrouded in secrecy behind closed corporate doors. Instead, development of J2ME is handled through the Java Community Process (JCP), which allows anyone with an Internet connection to get involved.
J2ME provides a complete set of solutions for creating state-of-the-art networked applications for small devices. It also promises to enable device manufacturers, service providers, and application developers to deploy new applications and services to their customers. However, in doing so, it does not sacrifice some of the founding guidelines of Java, which have become increasingly important these days, namely cross-platform compatibility and security.
From a high-level view, J2ME defines the following components:
The first two components make up the J2ME runtime environment. Figure 1-1 provides a relational view of the runtime environment. At its heart is a Java virtual machine, which runs on top of a device's host operating system. Above that is a specific J2ME configuration, which consists of programming libraries that provide basic functionality based on the resource requirements of the device. On top of the configuration are one or more J2ME profiles, which are additional programming libraries that take advantage of kindred functionalities on similar devices.
Figure 1-1. The high-level architecture of J2ME runtime environment
If you haven't worked with J2ME before, you're probably wondering about the top two layers. It's important to distinguish between a configuration and a profile in the J2ME world, so let's introduce them now.
Cellular telephones, pagers, organizers, and other small devices are diverse in form, functionality, and feature. However, they often use similar processors and have similar amounts of memory. For these reasons, the J2ME designers created configurations. Configurations define a horizontal grouping of products based on the available memory budget and processing power of each device. Once this information is known, the configuration then outlines the following:
Currently, there are two standard configurations in the J2ME world: the Connected Limited Device Configuration (CLDC) and the Connected Device Configuration (CDC). Let's look at the CDC first.
The CDC is targeted toward powerful devices that are intermittently connected to a network, including set-top boxes, Internet TVs, home appliances, and car navigation systems. The CDC contains a full-featured Java virtual machine, similar to that in use today with the J2SE. The difference lies in the respective devices' memory and display capabilities.
Here are the resource requirements for CDC devices, as given by the official J2ME specifications:*
The second type of configuration is more prevalent in the J2ME world: the CLDC.
This configuration specifies a much smaller footprint for consumer and embedded devices than the CDC. The CLDC was first distributed in October 1999 with the idea of creating a "lowest common denominator" Java platform for embedded devices, specifically in terms of networking, I/O, security, and core libraries. Today, some of the devices that you might find powered by the CLDC include mobile cell phones, two-way pagers, personal digital assistants (PDAs), and personal organizers.
Here are the requirements for the J2ME CLDC, again from the official J2ME specifications:*
* The J2ME CDC specifications are located on the Java Community Process web site as JSR-36, which can be found at http://www.jcp.org/jsr/detail/36.jsp.
* Note that CLDC stands for Connected Limited Device Configuration, not Connectivity-Limited Device Configuration, The difference between the CLDC and the CDC is not in the type or speed of the network connection.
The two products' configurations, along with some of their respective products, are shown in Figure 1-2.
Figure 1-2. J2ME architecture
Note that although the two product groups are supported by different configurations, the line between the two configurations is somewhat blurred. In the future, technological advances will likely make this boundary more and more cloudy.
However, for the moment, the important thing to remember is that the boundary between the CLDC and the CDC is defined in terms of the target device's memory budget, battery usage, and the presence or absence of a user interface.
As mentioned above, the CLDC and CDC configurations each define their own set of supported features from the Java virtual machine. Consequently, each requires its own Java virtual machine. The CLDC virtual machine is far smaller than the virtual machine required by the CDC, since it supports fewer features. The virtual machine for the CLDC is called the Kilo Virtual Machine (KVM), and the virtual machine for the CDC is called the CVM.
The KVM is a complete Java runtime environment for small devices. It's a true Java virtual machine as defined by the Java Virtual Machine Specification, except for some specific deviations that are necessary for proper functioning on small devices. It is specifically designed from the ground up for small, resource-constrained devices with a few hundred kilobytes' total memory.
The KVM was originally created as a research project called "Spotless" at the Sun Microsystems Laboratories. The aim of the virtual machine was to implement a Java virtual machine for the resource-constrained Palm Connected Organizer.*
* In fact, early incarnations of the KVM contained several UI libraries based on the "spotless" graphical toolkit.
The CVM is designed for larger consumer and embedded devices., such as those found with the CDC. It supports all Java 2 Version 1.3 virtual machine features and libraries for items such as security, weak references, JNI, and Remote Method Invocation (RMI). The reference implementation, currently available from Sun Microsystems, runs on Linux and VxWorks. You can download the reference implementation through the J2ME web site at http://java.sun.com/j2me/.
Initially, CVM was an acronym for "Compact" Virtual Machine. However, engineers at Sun Microsystems realized that snappy marketers (or poor spellers) may confuse the "compact" in CVM with the K in KVM. So, at present, the C does not stand for anything at all-it is simply known as the CVM.
J2ME makes it possible to define Java platforms for vertical product markets by introducing profiles. At the implementation level, a profile is a set of APIs that reside on top of a configuration that offers the program access to device-specific capabilities. Following are some examples of profiles that are currently offered through J2ME.
The MIDP is designed to be used with the CLDC, and provides a set of APIs for use by mobile devices, such as cellular phones and two-way pagers. The MIDP contains classes for user interface, persistence storage, and networking. It also includes a standardized runtime environment that allows new applications to be "downloaded" to end user devices. Small applications that run under the MIDP are called MIDlets. Since this profile is already released, the vast majority of this book is dedicated to the MIDP.
The PDA profile is based on the CLDC and provides user interface APIs (which are expected to be a subset of the AWT) and data storage APIs for handheld devices. As of this writing, the PDA profile is still in the works and no reference implementation is available yet.
The Foundation profile extends the APIs provided by the CDC, but it does not provide any user interface APIs. As the name "foundation" implies, this profile is meant to serve as a foundation for other profiles, such as the Personal profile and the RMI profile.
The Personal profile extends the Foundation profile to provide a graphical user interface (GUI) capable of running Java Web applets. Since PersonalJava is being redefined as the Personal profile, it will be backward compatible with PersonalJava 1.1 and 1.2 applications. As of this writing, no reference implementation of the Personal profile is available.
The RMI profile extends the Foundation profile to provide RMI for devices. Since it extends the Foundation profile, the RMI profile is meant to be used with the CDC/ Foundation and not the CLDC/MIDP. The RMI profile will be compatible with J2SE RMI API 1.2.x or higher. However, as of this writing, no reference implementation is available yet.
Figure 1-3 shows a global snapshot of current and future J2ME technologies.
Figure 1-3. J2ME environment
Downloading the J2ME Wireless Toolkit
Now that you know your way around the J2ME landscape, let's get started with J2ME. However, before we can compile and run any J2ME programs, we need to download and install the J2ME Wireless Toolkit. You can obtain the J2ME Wireless Toolkit at the following URL: http://java.sun.com/products/j2mewtoolkit/download.html.
The version that we use in this paper is 1.0.3 beta. It is available for the Microsoft Windows 98/ME and 2000 platforms, as well as Linux and Sun Solaris operating systems. The toolkit requires the presence of at least Version 1.3 of the Java Development Kit (JDK) for the host operating environment.
Once you've downloaded the Wireless Toolkit, double-click on it or execute the resulting binary (depending on your platform) to activate the extraction. This will uncompress the files needed to install the Wireless Toolkit. Note that you may be directed to specify an existing JDK installation on your system. If so, choose the latest stable release of the JDK that you currently have on your system.*
* Try to use a JDK instead of just a Java Runtime Environment (JRE). It's important that you have the javac compiler to create J2ME applications.
In addition, the distribution may also ask you if you would like to install a version of the toolkit that interfaces with ForteTM for Java. If you would like to develop your J2ME applications in the Forte for Java Integrated Development Environment, choose the corresponding option. Be sure that Forte is already installed on your system before doing so.
In this case, we're going to install the Java Wireless Toolkit on a Windows platform
into the directory C:\j2mewtk
. After the installation is completed, this directory will
contain all the required classes and tools to run the MIDP applications. (If the
installation program asks you to run the ktoolbar program, just ignore it for the moment.)
However, we need to do a few more things before we can get started with our
examples.
First, we need to add the wireless toolkit binaries to your system path. You can do
that on Windows with the following command (again, we've assumed that the Java
Wireless Toolkit is installed at C:\j2mewtk
):
SET PATH=%PATH%;C:\j2mewtk\bin
If you edit your C:\AUTOEXEC.BAT file to add this to the default system path, as shown below, and restart your machine, then you will not have to repeatedly perform this step each time you restart your system.
With Linux and Solaris, the equivalent command is:
export PATH=$PATH:install_directory/j2mewtk/bin
Once you've added that directory to your system path, you should be able to run the Java Wireless Toolkit tools from any directory on your system. An easy way to test it is to execute the preverify command, without any arguments. You should see output similar to the following:
C:\> preverify Usage: PREVERIFY.EXE [options] classnames|dirnames ... where options include: -classpath <directories separated by ';'> Directories in which to look for classes -d <directory> Directory in which output is written @<filename> Read command line arguments from a text file.
In order for the toolkit to work properly, you'll need to have the J2SE tools (notably javac) available on your system executable path as well. Instructions on how to do this are bundled with the JDK, although it really boils down to adding the binary path of the J2SE binaries to your system path.
If you're familiar with the J2ME Wireless Toolkit already, you're likely
wondering why we're not using KToolbar. We'll cover KToolbar in
this section.
In the meantime, it helps to see how J2ME works under the
hood.
To compile and run J2ME programs from the command line, enter the following commands. Again, feel free to set these system environment variables on the command line, or edit the AUTOEXEC.BAT file (or similar) on your system for convenience.
SET J2MEWTK_HOME=C:\j2mewtk SET MIDPAPI=%J2MEWTK_HOME%\lib\midpapi.zip SET J2MECLASSPATH=%J2MEWTK_HOME%\wtklib\kenv.zip; %J2MEWTK_HOME%\wtklib\kvem.jar;%J2MEWTK_HOME%\wtklib\lime.jar
On the Linux and Solaris side, the following could be added to your profile (or equivalent) :
export J2MEWTK_HOME=/home/qmahmoud/j2mewtk export MIDPAPI=$J2MEWTK_HOME/lib/midpapi.zip export J2MECLASSPATH=$J2MEWTK_HOME/wtklib/kenv.zip: $J2MEWTK_HOME/wtklib/kvem.jar:$J2MEWTK_HOME/wtklib/lime.jar
Note that the final line in either case is really one line; it's been continued here for clarity.
The examples that we're going to demonstrate here, and throughout the rest of the paper, are called MIDlets. If you've programmed with Java applets or servlets before, then you'll likely recognize the similarities in the "fill-in-the-method" program structure. This first example, HelloMidlet.java, shown in Example 1-1, creates a text box and then prints the archetypal "Hello World" in a text box.
Example 1-1. "Hello World" import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class HelloMidlet extends MIDlet { // The display for this MIDlet private Display display; // TextBox to display text TextBox box = null; public HelloMidlet() { } public void startApp() { display = Display.getDisplay(this); box = new TextBox("Simple Example", "Hello World", 20, 0); display.setCurrent(box); } /** * Pause is a no-op since there are no background activities or * record stores that need to be closed. */ public void pauseApp() { } /** * Destroy must cleanup everything not handled by the garbage collector. * In this case there is nothing to cleanup. */ public void destroyApp(boolean unconditional) { } }
This MIDlet consists of a public class definition that extends the MIDlet class found in javax.microedition.midlet. This superclass forms the base of all MIDlets in J2ME. Our HelloMidlet class contains a constructor, as well as the startApp(), pauseApp( ), and destroyApp( ) methods that have been inherited from the MIDlet class. Note that there is no main() method in this program. Instead, the startApp(), pauseApp( ), and destroyApp( ) methods are called by the underlying framework to start up the MIDlet, to pause it, or to destroy it.
Let's start off by compiling our program on the command line. Using the command line is a bit more complex than the KToolbar application that comes with the Wireless Toolkit, so in order to simplify it, be sure that you have entered the additional environment variables shown above. However, there are several steps that we need to perform when compiling J2ME applications, and it's important to see each of the steps as they occur.
As you would expect, the program must be saved in a file called HelloMidlet.java. However, before you compile it, create a directory called tmpclasses. Then use the following command to compile the MIDlet from the command line in Windows:
C:\midlets> javac -g:none -d tmpclasses -bootclasspath %MIDPAPI% -classpath %J2MECLASSPATH% HelloMidlet.java
In Linux and Solaris, the command looks like the following:
>javac -g:none -d tmpclasses -bootclasspath $MIDPAPI -classpath $J2MECLASSPATH HelloMidlet.java
This command compiles the Java source file without any debugging info, and sets the appropriate boot and J2ME classpaths to ensure that we don't pick up any J2SE classes. The end result of this command is the creation of the HelloMidlet.class file in the tmpclasses directory.
With the J2SE, a class file was all you needed to run the application. However, all MIDlet classes must be preverified before they can be run on a target device. Why is this necessary? Remember that one of the tasks of the standard Java virtual machine (the one that comes with the J2SE) is to perform bytecode verification. Bytecode verification is one of the most important steps of the Java security model. It performs such tasks as ensuring that the bytecodes of a Java class (and their operands) are all valid; that the code does not overflow or underflow the VM stack; that local variables are not used before they are initialized; that field, method, and class access control modifiers are respected, and other important tasks. However, most of the bytecode verifier is not included with the KVM due to size constraints. The preverifier ensures that the equivalent security checks still take place.
Before you run the preverifier, create another directory called classes. Then, use this command to preverify the HelloMidlet class:
C:\midlets> preverify -classpath %MIDPAPI%;tmpclasses -d classes tmpclasses
Or on Solaris and Linux:
> preverify -classpath $MIDPAPI:tmpclasses -d classes tmpclasses
The resulting output should look something like this:
[Output directory for verified classes: classes]
This command takes all the classes inside the tmpclasses directory (of which HelloMidlet.class is the only one) and preverifies them, writing the resulting classes to the classes directory. Note that the names of the preverified classes remain exactly the same, which is why we created two separate directories to hold them.
If you received an "Illegal constant pool index" class loading error and
you're using JDK 1.4, try using JDK 1.3 until this issue is resolved.
The next step is to compress all the classes in the program (again, we have only one) as well as their resources, into a Java Archive (JAR) file. You can use the J2SE jar command to create a JAR file. Make sure you are in the classes directory to execute the following command:
> jar cvf HelloMidlet.jar HelloMidlet.class
The program will compress the HelloMidlet class into a JAR file, creating a manifest for it as well.
Note that with the javac compiler, you can create MIDlets of practically any size. However, that doesn't guarantee that they will fit on the target device for which you're writing the MIDlet. It would nice if there were a way to check if the target device can handle the MIDlet and run it before it is downloaded. Obviously, if a device can't handle the MIDlet, there is no reason to even attempt a download.
To accomplish this, we need a file that manually specifies some pre-download properties, including the size of the MIDlet and its storage requirements. This can be accomplished by creating a Java Application Descriptor (JAD) file with your favorite text editor. Example 1-2 shows a sample JAD file that we can use. Note that you will need to change the MIDlet-Jar-Size entry to correspond to the size of the JAR file that you just created. (In this section, we will explain the JAD file syntax in more detail.)
Example 1-2. HelloMidlet.jad
MIDlet-1: Hello,,HelloMidlet MIDlet-Name: HelloMidlet MIDlet-Version: 1.0 MIDlet-Vendor: ORA MIDlet-Jar-URL: HelloMidlet.jar MIDlet-Jar-Size: 863
Let's save this example JAD file as HelloMidlet.jad, again in the classes directory that holds the JAR file. Finally, to run this MIDlet, invoke Sun's MIDP emulator to point at the JAD file using the following command:
> emulator -Xdescriptor:HelloMidlet.jad
If everything worked correctly, you should see a phone similar to Figure 1-4, although the display may be different. Here, the HelloMidlet is running in the default phone that comes with the Java Wireless Toolkit. If you click on the MIDlet on the menu (use the directional arrow pad to move the cursor and the button in the middle to select), and instruct it to "Launch" using the soft button on the lower right, you should see output similar to Figure 1-4. Congratulations! You just created your first Java MIDlet!
Figure 1-4. HelloMidlet
The gist of this program is in the startApp( ) method. Here, we obtain the current display that the device uses, then create a text box with the words "Hello World" inside of it. Finally, we show the text box on the current display. Don't worry if you don't understand these objects yet; the architecture of MIDlets will become clearer as we move through the paper.
Let's move to a more advanced MIDlet. Example 1-3 shows a MIDlet with a hypothetical login screen that prompts the user to log in. If the login is incorrect, the program will repeatedly ask the user to try again.
Example 1-3. A login MIDlet
import javax.microedition.midlet.MIDlet; import javax.microedition.lcdui.*; public class LoginMidlet extends MIDlet implements CommandListener { private Display display; private TextField userName; private TextField password; private Form form; private Command cancel; private Command login; public LoginMidlet() { userName = new TextField("LoginID:", "", 10, TextField.ANY); password = new TextField("Password:", "", 10, TextField.PASSWORD); form = new Form("Sign in"); cancel = new Command("Cancel", Command.CANCEL, 2); login = new Command("Login", Command.OK, 2); } public void startApp() { display = Display.getDisplay(this); form.append(userName); form.append(password); form.addCommand(cancel); form.addCommand(login); form.setCommandListener(this); display.setCurrent(form); } public void pauseApp() { } public void destroyApp(boolean unconditional) { notifyDestroyed(); } public void validateUser(String name, String password) { if (name.equals("qm") && password.equals("j2")) { menu(); } else { tryAgain(); } } public void menu() { List services = new List("Choose one", Choice.EXCLUSIVE); services.append("Check Mail", null); services.append("Compose", null); services.append("Addresses", null); services.append("Options", null); services.append("Sign Out", null); display.setCurrent(services); } public void tryAgain() { Alert error = new Alert("Login Incorrect", "Please try again", null, AlertType.ERROR); error.setTimeout(Alert.FOREVER); userName.setString(""); password.setString(""); display.setCurrent(error, form); } public void commandAction(Command c, Displayable d) { String label = c.getLabel(); if(label.equals("Cancel")) { destroyApp(true); } else if(label.equals("Login")) { validateUser(userName.getString(), password.getString()); } } }
Again, don't worry if you can't understand the entire program at this point; this example is just meant to give you a flavor of MIDP programming and some sample applications to compile and run.
That being said, let's present a beginner's overview of how this MIDlet works. As in the previous example, LoginMidlet extends the MIDlet abstract class. It also implements the CommandListener interface by providing an implementation for the commandAction( ) method. In this method, there are two commands: Login and Cancel. The label of the command is checked: if it is Cancel, the LoginMidlet is destroyed, and if it is Login, then the username and passwords are validated.
In the LoginMidlet's constructor, a Form object, two TextField objects, and two Command objects are created. The TextField and Command objects are added to the form in the startApp() method. In addition, pauseApp() and destroyApp( ) perform minimal tasks.
Here is how the program operates: if the Login command is given, the application calls the validateUser( ) method to validate the username and password. If they are valid (in this case, they are hardcoded into the, program for simplicity), then the menu( ) method is called to simulate a list of "useful services." Otherwise, the tryAgain( ) is called to display an error message and to allow the user to reenter their name and password.
If you are using the command line to compile and execute, save this file named LoginMidlet.java, make sure that you have a classes and a tmpclasses directory, and use javac:
C:\midlets> javac -g:none -d tmpclasses -bootclasspath %MIDPAPI% -classpath %J2MECLASSPATH% LoginMidlet.java
If you are using Solaris or Linux, the command becomes:
>javac -g:none -d tmpclasses -bootclasspath $MIDPAPI -classpath $J2MECLASSPATH LoginMidlet.java
Next, remember that we must preverify the resulting class:
C:\midlets> preverify -classpath %MIDPAPI%;tmpclasses -d classes tmpclasses
or
> preverify -classpath $MIDPAPI:tmpclasses -d classes tmpclasses
Again, the preverified class is saved to the classes subdirectory in the current directory. Next, compress the resulting class into a JAR file:
jar cvf LoginMidlet.jar LoginMidlet.class
And finally, create a JAD file that describes the resulting JAR file in detail, as shown in Example 1-4.
Example 1-4. LoginMidlet.jad
MIDlet-1: Login" LoginMidlet MIDlet-Name: LoginMidlet MIDlet-Version: 1.0 MIDlet-Vendor: ORA MIDlet-Jar-URL: LoginMidlet.jar MIDlet-Jar-Size: 1786
Again, don't forget to change the size of the JAR file to match the size of the LoginMidlet.jar file after you create it.
At this point, the MIDlet can be run as in the previous example, using the MIDP emulator of the Java Wireless Toolkit, with the following command:
emulator -Xdescriptor:LoginMidlet.jad
In addition, the MIDlet can be run with any other emulator that you may have available. For example, to whet your appetite, Figure 1-5 shows the LoginMidlet running on default ColorPhone emulator.
Note that the objects represented by the Command class are shown above the two "soft buttons" on the phone (the buttons with the black circles). If a soft button below the command is pressed, the command immediately above it is executed. Here, if the user enters the correct username and matching password and presses the Login button, the menu of services will be displayed. Otherwise, the alert will be displayed and the user can try again.
Figure 1-5. LoginMidlet running in default ColorPhone emulator (cropped)
Also, you might be caught off guard the first time you try to enter text with your computer keyboard. It doesn't work! That's because you must use the input keys on the phone to enter the text. In this case, to enter the letter "G", press the number "4". To enter the letter "K", press the number "5" twice. Note how each time you press a numeral, the system "cycles" through the letter corresponding to that number. To move down to entering text for the password, use the down arrow.
Well, that's it! You've just created two professional MIDlets using J2ME! In the next two sections, we're going to take a much closer look at the CLDC and the MIDP, two exciting new areas of wireless Java development.
The Connected Limited Device Configuration (CLDC)
The Connected Limited Device Configuration (CLDC) defines a standard, minimum-footprint Java platform for small, resource-constrained devices. As we mentioned in the first section, the CLDC was designed as a lowest common denominator of Java that can be applicable to a wide variety of devices. However, features specific to a certain vertical market, such as cell phones or pagers, are not found in the CLDC but are instead defined in profiles that sit above it. Configurations primarily target devices with similar amounts of memory and processing power.
This leads to a very important point about the CLDC: there are no optional features. Everything that the CLDC provides is usable on the devices that support it. After all, the primary goal of the CLDC is to ensure portability and interoperability between applications running on various kinds of resource-constrained devices, which is the main objective of programming in Java. In this section, we discuss the CLDC and its virtual machine, the KVM, in detail.
Let's start off with some specifics. According to the specification, the devices targeted by the CLDC have the following characteristics:
160 KB to 512 KB of total memory
At a minimum, a CLDC device should have 128 KB of non-volatile memory for the Java VM and,the CLDC libraries, and at least 32 KB of volatile memory for the VM to use at runtime, independent of any applications.
16-bit or 32-bit processor with at least 25 Mhz speed
These types of processors are pretty typical in today's handheld devices.
Connectivity to some kind of networking
With CLDC, this is often a two-way wireless connection with limited bandwidth.
Low power consumption
CLDC devices often operate under battery power. Hence, they have very low power consumption.
Devices that fit these characteristics come in all shapes and sizes. Cell phones and pagers immediately come to mind, but one could also install Java on bar code scanners, video and audio equipment, navigation systems, and other wireless devices yet to come. In fact, as the nature of these devices changes, you can expect that the base specifications for the CLDC will change as well.
Given the constraints listed above, the CLDC currently provides the following functionality to its devices:
Note, however, that the CLDC does not address application life cycle management, user interfaces, event handling, or the interaction between the user and the application. Again, these features fall into the domain of profiles, such as the MIDP, which are implemented on top of the CLDC and add to its functionality.
What's Different About the Java Virtual Machine?
We mentioned that the CLDC does not have any optional features. As you might expect, this means that a number of features have been eliminated from Java virtual machines that support the CLDC, either because they are too expensive (in terms of memory or processing capability) to implement, or because their presence would impose security problems. Therefore, if you're new to programming with the CLDC, you should be aware of the following limitations in CLDC VMs:
No floating point support
The CLDC does not support floating point numbers; therefore, CLDC-based applications cannot use floating point types such as float or double. This decision was made because most CLDC target devices do not have floating point support in their underlying hardware.
No finalization
The CLDC API currently does not include the Object.finalize( ) method; you cannot perform final cleanup operations on object data-such as closing resources-before an object is garbage-collected.
Limited error handling
Runtime errors are handled in an implementation-specific manner. The CLDC defines only three error classes: java.lang.Error, java.lang.OutOfMemoryError, and java.lang.VirtualMachineError. Non-runtime errors are handled in a device-dependent manner that often involves terminating the application or even resetting the device.
No Java Native Interface (JNI)
A Java virtual machine supporting the CLDC does not implement the JNI. There are actually two good reasons for this: security, and the fact that implementing JNI is expensive, given the memory constraints of CLDC target devices.
No user-defined class loaders
A Java virtual machine supporting the CLDC must have a built-in class loader that cannot be overridden or replaced by the user. This is for security reasons.
No support for reflection
CLDC applications do not have the ability to use the reflection APIs on their objects or classes. Because reflection is not supported, there is also no support for object serialization or RMI.
No thread groups or daemon threads
While a Java virtual machine that supports the CLDC will implement multithreading, it cannot support thread groups or daemon threads. If you want to perform thread operations for groups of threads, use the collection objects to store the thread objects at the application level.
No weak references
No application built on a Java virtual machine supporting the CLDC can require weak references.
The KVM, which was introduced in the previous section, is a complete Java runtime environment for small devices. It is a true Java virtual machine as defined by the Java Virtual Machine Specification, except for some deviations that are necessary for proper functioning on small devices. The KVM was specifically designed for small, resource-constrained devices that have only a few hundred kilobytes total memory.
The J2ME white paper* describes the KVM as:
* See also the KVM white paper, located at http://java.sun.com/products/cldc/wp/KVMwp.pdf, for much more detail on the KVM.
The KVM was derived from a research project called Spotless at Sun Microsystems Laboratories. The aim of the project was to implement a Java system for the Palm Connected Organizer.*
* If you attended JavaOne 1999, you'll remember that this was a major attraction. They even held a contest to see who could design the best KVM application.
The KVM is written in the C programming language (using about 24,000 lines of code), so it can be easily ported to various platforms for which a C-language compiler is available. Finally, like a regular JVM, the KVM can load classes from a class path directory as well as from a JAR file.
In the J2SE Java virtual machine, the class verifier is responsible for rejecting invalid class files at runtime. A JVM supporting CLDC must be able to reject invalid class files as well. The class verification process, however, is expensive and time-consuming: it typically takes anywhere from 35 to 110 KB of runtime memory. Since the target size of the KVM is 50 to 80 KB of memory, including a class verifier inside it would violate its size constraints.
The KVM designers decided to move most of the verification work off the device and onto the desktop, where the class files are compiled, or onto the server machine, from which applications are downloaded. This step (off-device class verification) is referred to as preverification; that's why we had to run the preverify command on the examples in the first section. Once the preverification is completed, the resulting c)ass files often include extra information to ensure that the runtime verifier can perform its job with only minimal effort and memory. (That's why the preverified version of the LoginMidlet.class in the 1-st section is slightly larger than the raw class generated by the javac compiler.)
The additional output of the preverification process is the addition of a stack map attribute that maps out critical areas of a class. This additional attribute is used by the runtime verifier to pinpoint critical areas inside the class that must be checked. Also, the preverifier will inline all subroutines in the bytecodes of the class file to prevent any problems at runtime. Don't worry, however. Even with the additional information, the preverified class files can still work with a regular Java runtime verifier.
With the help of the preverification, the CLDC device is only responsible for running a quick scan on the preverified class file to ensure that it was verified and does not contain any illegal instructions. This cuts down significantly on the amount of memory needed for the runtime verifier: only 100 bytes or so.
The CLDC security model is more strict than what you're likely used to with the J2SE. This new security model is primarily concerned with two areas:
Virtual machine-level security
An application executed by the KVM must not be able to harm the device in which it is running. This is guaranteed by the class verifier, which ensures that the class bytecodes cannot contain references to invalid memory locations. It also ensures that the classes loaded cannot execute in a way that is not allowed by the Java Virtual Machine Specification. As we mentioned, class verification for the CLDC/KVM is a two-step process: off-device preverification in conjunction with a minimal in-device verification. In addition, native methods cannot be invoked at runtime.
Application-level security
Unlike the J2SE, the CLDC/KVM combination does not allow the customization of a security manager. A JVM supporting CLDC provides a simple sandbox security model that enforces security by ensuring that applications run in a closed environment, and that applications may only call classes supported by the device.
What's Different About the Core Java Libraries?
The first thing that you'll probably notice when working with the CLDC is that only a bare minimum of Java APIs have been included. The reason for this is obvious if you download the Java 2 SDK: the standard edition APIs require close to 20 megabytes of memory! This is memory that most small devices simply do not have. Hence, one of the primary goals in designing the core CLDC libraries was to boil the J2SE APIs off into a minimum set of libraries that could still be used for meaningful application and profile development.
With that in mind, it's helpful to think of the CLDC library APIs as divided into two categories: classes that are a subset of the J2SE APIs and new classes that are specific to the CLDC. Let's look at the former group first.
The CLDC uses only thirty-seven classes from the J2SE platform. These classes come from the java.lang, java.io, and java.util packages, which are derived from JDK 1. 2 APIs. Note that according to the J2ME specification, "Each class that has the same name and package name as a J2SE class must be identical to, or a subset of, the corresponding J2SE class. The semantics of the classes and methods cannot be changed, and the classes cannot add any public or protected methods or fields that are not available in the corresponding J2SE class libraries." In other words, you cannot add, but you can take away. And many classes have functionality taken away.
The inherited classes and interfaces (not including exceptions) from the J2SE platform are shown in Table 2-1.
Table 2-1. Inherited, non-exceptional classes
Package | Classes |
---|---|
java.lang | Boolean, Byte, Character, Class, Integer, Long, Math, Object, Runnable, Runtime, Short, String, StringBuffer, System, Thread, Throwable |
java.io | ByteArrayInputStream, ByteArrayOutputStream, DataInput DataOutput, DataInputStream, DataOutputStream, InputStream, OutputStream, InputStreamReader, OutputStreamWriter, PrintStream, Reader, Writer |
java.util | Calendar, Date, Enumeration, Hashtable, Random, Stack, TimeZone, Vector |
Because all inherited classes must throw precisely the same exceptions as regular J2SE classes, the following 29 exception and error c)asses shown in Table 2-2 also derive from the J2SE APIs.
Table 2-2. Inherited exception and error classes
Package | Class |
---|---|
java.lang | ArithmeticException, ArrayIndexOutOfBoundsException, ArrayStoreException, ClassCastException, ClassNotFoundException, Error, Exception, IllegalAccessException, IllegalArgumentException, IllegalMonitorStateException, IllegalThreadStateException, IndexOutOfBoundsException, InstantiationException, InterruptedException, OutOfMemoryException, NegativeArraySizeException NumberFormatException, NuIIPointerException, RuntimeException, SecurityException, StringIndexOutOfBoundsException, VirtualMachineError |
java.io | EOFException, IOException, InterruptedIOException, UnsupportedEncodingException, UTFDataFormatException |
java.util | EmptyStackException, NoSuchElementException |
When programming with the CLDC, there are many internal modifications to the J2SE classes you're used to. Here are some of the more common classes that may cause problems.
The following methods have been removed from the ubiquitous java.lang.String class, either because they refer to floating-point data types or because their presence is redundant:
public void valueof(float f) public void valueof(double d) public int compareToIgnoreCase(String str) public boolean equalsIgnoreCase(String anotherStr) public static copyValueOf(char[] data) public static String copyValueOf(char[] data, int offset, int count) public String intern() public int lastIndexOf(String str) public int lastIndexOf(String str, int fromIndex) public boolean regionMatches(int toffset, String other, int ooffset, int len) public String toLowerCase(java.ufil.Locale locale) public String toUpperCase(java.util.Locale locale)
For the same reasons, the following methods have been eliminated from the java.
lang.StringBuffer class.
public StringBuffer append(float f) public StringBuffer append(double d) public StringBuffer insert(int offset, float f) public StringBuffer insert(int offset, double d) public StringBuffer insert(int index, char[] str, int offset, int len) public StringBuffer replace(int start, int end, String str) public String substring(int start) public String substring(int start, int end)
The java.lang.Runtime class has eliminated most of its methods for the CLDC. Here, only the following subset of methods is now available:
public void exit(int status); public native long freeMemory(); public native void gc(); public static Runtime getRuntime(); public native long totalMemory();
In addition, the java.lang.System class only has the following fields and methods available to it:
public static final PrintStream err; public static final PrintStream out; public static native void arraycopy (Object src, int src_position, Object dst, int dst_position, int length); public static native long currentTimeMillis(); public static void exit(int status); public static void gc(); public static String getProperty(String key); public static native int identityHashCode(Object x);
Finally, as you might expect, the java.lang.Math class has eliminated all methods dealing with. complex floating-point operations (which was the vast majority of methods in that class); and now only has the following methods:
public static int abs(int a); public static long abs(long a); public static int max(int a, int b); public static long max(long a, long b); public static int min(int a, int b); public static long min(long a, long b);
In many cases, the absence of these methods are only a minor inconvenience and suitable workarounds can be used. J2ME functionalities that require the use of float-point values, however, may have to expand their floating-point values to integers with an implied decimal point and improvise with the more limited set of integer operations.
What's Different About I/O and Networking?
Recall that the J2SE provides the java.io and java.net packages for I/O and network connectivity. The CLDC inherits some of the classes in the java.io package. However, the major difference is that it does not inherit classes related to file I/O. For example, the popular FileInputStream and FileOutputStream classes are not present. In addition, the FileReader and FileWriter classes are not offered for reading and writing text data. This because not all CLDC devices support the concept of a filesystem.
As for the java.net package, the J2SE provides several classes for network connectivity. However, none of these classes have the inherited because not all devices require TCP/IP or UDP/IP. (Some devices may not even have a IP stack.) Instead, the CLDC expert group decided to define a more generic set of classes for J2ME I/O and network connectivity. The classes are known as the Generic Connection Framework, and are found in the javax.microedition.io package.
The Generic Connection Framework
The Generic Connection Framework is a platform-independent framework that provides its functionality without any dependency on the specific features of a device. In fact, this framework is so generic that it doesn't implement any of the I/O or network connectivity interfaces; rather, profile above it provides such implementation.
Here's a quick rundown of how the Generic Connection Framework works: all connections are created using the static open() method from the factory Connector class. If successful, this method returns an object that implements one of the generic connection interfaces for the device. I you're a J2SE programmer, this will be much different that you are used to. However, it will also be much easier. To give you a taste of what this is like, here are some example connections from the J2ME specification that you might request from a CLDC application and the appropriate syntax to implement them:
HTTP connection
Socket connectionConnector.open("http://www.ora.com:port");
Communication with a portConnector.open("socket://www.ora.com:port");
Connector.open("comm:0;baudrate=9600");
The goal of the above syntax is to isolate any differences in the protocol that you're attempting to connect with into a simple string. This way, most of the application's code remains the same, regardless of the protocol you use. The Generic Connection
Differences with Property Support in the CLDC
Virtual machines that support the CLDC, such as the KVM, do not implement the java.util.Properties class, which follows from the lack of filesystem functionality that we mentioned above. However, four system properties are supported for each J2ME/CLDC device, and can be accessed by using the method System.getProperty(String key). The four properties available are described in Table 2-3.
Table 2-3. System properties
System property | Description | Default value |
---|---|---|
microedition.platform | Name of the host platform or device | null |
microedition.encoding | Default character encoding | "1S08859_1" |
microedition.configuration | Name and version of the support configuration | "CLDC-1.0" |
microedition.profiles | Name of the supported profiles | null |
There's very little to say here, except that you can use these properties to ensure that you're indeed on a CLDC device that supports the proper encoding and profiles for your application. The MIDP profile defines some additional properties, which we will discuss in next section.
Using the Standalone CLDC and KVM
If you want to experiment with the raw KVM and CLDC classes, you can download the standalone CLDC and KVM. As of this writing, the latest edition of the CLDC itself is version 1.0.2. The CLDC 1.0.2 contains an updated version of the KVM. The KVM code has been rewritten to improve performance and includes a faster bytecode interpreter, better garbage collection, Java-level debugging APIs, preverifier improvements, and several bug fixes. If you wish to download the standalone CLDC and KVM, you can find it at the following address: http://java.sun.com/products/kvm/.
Note that this is different than the J2ME Wireless Toolkit that we
used in the 1-st section.
This distribution does not contain any MIDP
classes, nor does it contain a MIDP emulator. Hence, it will only
execute programs that adhere to the base CLDC specification and not any
MIDP functionality. If you are solely interested in writing applications
for the MIDP, you can just read through this section without taking
any action.
This distribution contains KVM implementations for Windows, Solaris, and Linux operating systems, as well as the CLDC classes that can be used to compile and run applications. After downloading and uncompressing the distribution, you should have a series of directories, as shown in Table 2-4.
Table 2-4. CLDC/KVM directories
Directory | Description |
---|---|
api | The Java classes and source code for the CLDC |
bin | Binaries for each of the target platforms |
build | Utility that builds directories and makefiles for each target platform |
docs | PDF documentation, as well as compressed javadocs |
jam | Java Application Manager, which can be used to dynamically download classes into the KVM |
kvm | Source and build files pertaining to the KVM |
samples | Sample code that can be used with the CLDC |
tools | Source for the various tools used with the CLDC |
Feel free to look through the api directory to see what you have. It's not much, compared to the J2SE. In any case, there are some interesting things that we can show you with the KVM and the CLDC in this distribution. First, create a simple program that can be run with the CLDC as follows:
public class CLDCTest { public static void main(String[] args) { System.out.println("Hello CLDC!"); } }
Then, try compiling the program with the standard javac compiler. In the example below, we use a command line, similar to that in the first section, in order to ensure that only the CLDC classes are used. Note that the subsequent series of commands assumes that you are in the base directory of the CLDC/KVM distribution:
javac -bootclasspath api/classes.zip CLDCTest.java
Remember that we must preverify our resulting class before running it with the KVM, like we did in first section. You can do so with the following command:
bin/[targetOS]/preverify -classpath api/classes.zip:. CLDCTest
As before, this should create a separate directory, here called output, where the preverified class has been stored. You can now run this class with the KVM, using the following command:
bin/[targetOS]/kvm -classpath api/classes.zip:output CLDCTest Hello CLDC!
Next, try modifying the source code so that it adds a declaration of the float variable:
public class CLDCTest { float f; public static void main(String[] args){ System.out.println("Hello CLDC!"); } }
And again, try recompiling it and preverifying it with the above commands. If you try running the resulting program, the KVM will flag the inclusion of a floating-point field in the class as an error:
ALERT: Bad field signature
Why didn't the compiler flag the use of the floating-point variable as an error? Remember that you're using the javac compiler from J2SE to compile your J2ME programs, and that compiler is all too familiar with the use of floating-point variables. Hence, it will assume that the primitive data types that it knows about are fine for use with whatever JVM is on the other side of the compilation. In addition, the preverifier will not search for floating-point variables because its job (at least, on the desktop side) is to look for security issues within classes, not to hunt down invalid primitive data types. (Remember we mentioned earlier that preverified class files must work under the regular J2SE.) Hence, the KVM itself has to tell us that one of our fields is not supported in the virtual machine, which it does by scanning through the class files before executing them. There's an important lesson here: just because the compiler and preverifier successfully translated a source file to a class with only the CLDC classes on its bootclasspath doesn't mean that it will still run. You should always test it with the KVM as well, to see if the code has any VM issues.
That's not to say that if we used a method that is no longer in the CLDC classes, the compiler wouldn't notice. For example, assume that we modified our code to be the following:
public class CLDCTest { static String s = "Hello CLDC!"; static int r = s.compareToIgnoreCase("HELLO CLDC!"); public static void main(String[] args) { System.out.println(s + "." + r); } }
This yields the following compiler error:
CLDCTest.java:4: cannot resolve symbol
symbol: method compareToIgnoreCase(java.lang.String)
location: class java.lang.String
Here, the compiler flagged an error because the String class that was located on its bootclasspath does not contain the method in question, compareToIgnoreCase( ). As we mentioned earlier in the section, this method has been omitted in the CLDC subset of java.lang. String.
Finally, let's briefly mention the CLDC Next Generation (NG). The CLDC NG is a specification that is currently in development and that aims to define a revised version of the CLDC. The goal of the CLDC NG is to make the CLDC more compliant with the Java language and virtual machine specifications by reintroducing features such as floating-point support and improved error-handling capabilities.
Some other goals of the CLDC NG will be to:
Note, however, that not many new APIs will be reintroduced to the CLDC with this revision. Devices that require significantly more complete Java libraries should use the Connected Device Configuration (CDC) instead. You can follow the progress of the CLDC NG at the Java Community Process web site at: http://www.jcp.org/.
The Mobile Information Device Profile (MIDP)
The Mobile Information Device Profile (MIDP) is built on top of the CLDC, and defines an open application development environment for what Sun calls Mobile Information Devices (MIDs). In simpler terms, MIDP is the J2ME profile that is used for wireless devices, such as mobile phones and pagers. This section expands on the previous section by introducing some of the fundamental concepts of MIDP and offering programming guidelines that are used throughout the remainder of this paper.
As we mentioned in the first section, the MIDP is governed by the Java Community Process. The MIDP is JSR 37, which is part of the Java Community Process. Like the CLDC, the MIDP is an ever-changing standard that actively solicits input from corporations and the general programming community. You can find more information on the MIDP at the following URL: http://java.sun.com/products/midp/.
Again, let's start off with some specifics. The MIDP standard defines a MID as a device with the following minimum characteristics:
Display
A screen size of at least 96 x 54 pixels with at least a 1-bit display depth
Input
A one-handed keyboard, two-handed keyboard, or touch screen
Memory
32 KB of volatile memory for the Java runtime (heap); 128 KB of non-volatile memory for the MIDP components; and 8 KB of non-volatile memory for application-created persistent data
Networking
A two-way intermittent connection, usually wireless, with limited bandwidth
Because the MIDP is built on top of the CLDC, it addresses the following areas that are omitted by the CLDC:
Application Life Cycle Management
The MIDP includes the javax.microedition.midlet package, which contains classes and methods for starting, pausing, and destroying applications in the host environment.
User Interface and Events
The MIDP also provides the javax.microedition.lcdui packages, which include classes and interfaces for creating GUI components for applications.
Network Connectivity
The MIDP extends the ContentConnection interface of the Generic Connection Framework by providing the HttpConnection interface, as well as a subset implementation of the HTTP protocol.
Storing Data on Device
The MIDP also provides the javax.microedition.rms package, which implements a record-based database management system. This provides applications with the capability to store data on the device.
The MIDP has received wide corporate backing, from companies such as AOL, DDI, Ericsson, Fujitsu, Hitachi, Matsushita, Mitsubishi, Motorola, NEC, Nokia, NTT DoCoMo, Palm, Research in Motion (RIM), Samsung, Sharp, Siemens, Sony, Sprint, and Symbian. In the second quarter of 2001, Motorola released the first MIDPenabled cellular phones, the i50x and the i85s.* Over the next year or two, you'll likely see an impressive amount of MIDP-enabled devices reach the market.
The MIDP adds the following packages to those available through the CLDC, as shown in Table 3-1.
Table 3-1. New packages in the MIDP
Package | Description |
---|---|
javax.microedition.lcdui | Graphical interface components and events |
javax.microedition.midlet | Application life cycle |
iavax.microedition.rms | Record storage |
Here are the classes that are included with each of the new packages. The first package, javax.microedition.lcdui, contains interfaces and classes, listed in Table 3-2, that are used to build graphical interfaces on the limited displays of CLDC devices.
* The service for the i50x and i85s phones is provided in the United States and Canada by Nextel, Inc.
Table 3-2. Classes and interfaces in the javax.microedition.lcdui package
Name | Type |
---|---|
Choice | Interface |
CommandListener | Interface |
ItemStateListener | Interface |
Alert | Class |
AlertType | Class |
Canvas | Class |
ChoiceGroup | Class |
Command | Class |
DateField | Class |
Display | Class |
Displayable | Class |
Font | Class |
Form | Class |
Gauge | Class |
Graphics | Class |
Image | Class |
ImageItem | Class |
Item | Class |
List | Class |
Screen | Class |
StringItem | Class |
TextBox | Class |
TextField | Class |
Ticker | Class |
The next package, javax.microedition.midlet (see Table 3-3), adds only one class that serves as the base class for all MIDlets. This class can only throw one exception as well, which notifies listeners of a state change in the MIDlet. This class is discussed in detail in next section.
Table 3-3. Class and exception in the javax.microedition.midlet package
Name | Type |
---|---|
MIDlet | Class |
MIDletStateChangeException | Exception |
Finally, the javax.microedition.rms package provides four interfaces, one class, and five exceptions (see Table 3-4) for performing persistent data storage or MIDP devices. The four interfaces allow you to create implementing classes that customize how the record store compares, enumerates through, filters, and handles events that occur with data records.
Table 3-4. The classes, interfaces, and exceptions in the javax.microedition.rms package
Name | Type |
---|---|
RecordComparator | Interface |
RecordEnumeration | Interface |
RecordFilter | Interface |
RecordListener | Interface |
RecordStore | Class |
InvalidRecordIDException | Exception |
RecordStoreException | Exception |
RecordStoreFullException | Exception |
RecordStoreNotFoundException | Exception |
RecordStoreNotOpenException | Exception |
In addition to these packages, the MIDP also adds two classes and one exception to those classes in the java.lang and java.util packages of the CLDC. These classes are similar to those found in the Java SDK 1.3.
As you can see, there aren't many classes in the MIDP. However, that's not unexpected, given that we need to fit MIDP programs in such a limited space. But don't worry. We'll discuss each of the new classes, interfaces, and exceptions of the MIDP as we progress through the remainder of the paper.
The MIDP defines two additional property values (in addition to the eight in the previous section) that can be retrieved using the java.lang.System.getproperty( ) method. These are shown in Table 3-5:
Table 3-5. System properties defined by the MIDP
System property | Description |
---|---|
microedition.local | The current locale of the device (default: null) |
microedition.profiles | Must contain at least "MIDP-1.0" |
The microedition.local property consists of the language and country code separated by a dash "-". For example, "en-CA" for English Canada and "en-US" for English USA. Note that the language code must be lowercase, and the country code must be uppercase.
In the first section, we introduced you to MIDlets, applications that run on MIDP devices. MIDlets are written as one or more Java classes whose objects are compressed into a JAR file. Like Java applets, MIDP applications have an application life cycle while running on a mobile device. Specifically, a MIDlet can be in one of three states:
Figure 3-1 shows the rules for transitioning between states.
Figure 3-l. MIDlet transition states
Here is a quick rundown of how MIDP applications change state: when a MIDlet is first started, it is placed in the paused state. After it's ready, the controlling software will then place the MIDlet in the active state. At this point, the MIDlet is running and the user can interact with it. The application can be placed back in the paused state by either the MIDP system or the program itself. In addition, the MIDP can be moved to the destroyed state from either the paused or the active state, again by either the MIDP system or the programmer. In the destroyed state, the MIDlet should release all of the resources it currently has back to the MIDP system.
We'll cover this in more detail in the following section, where we create and execute a MIDlet with multiple states. In the meantime, this quick introduction brings us to the point where we must first introduce some important concepts.
A MIDlet suite is simply two or more MIDlets that are packaged in a JAR file. MIDlets within the same suite can use the classes and resources contained in the JAR file, much like any standard Java application, the classes of which are loaded by the same class loader.
The JAR file of a MIDlet suite often contains a manifest file with MIDlet attributes defined. These attributes describe the contents of the JAR file, which is in turn used by the application management software to identify and install the MIDlet suite. The attributes defined by the MIDP specification are listed in Table 3-6.
Table 3-6. JAR manifest attributes
Attribute name | Required | Description |
---|---|---|
MicroEdition-Configuration | Yes | The name and version of the J2ME configuration required. This use the same format as the MicroEdition.configuration system property (for example, "CLDC-1.0"). |
MicroEdition-Profile | Yes | The name and version of the J2ME profile required. This uses the same format as the microedition.profiles system property (for example, "MIDP-1.0"). |
MIDlet-n | Yes | The name, icon, and class, separated by commas, of the nth MIDlet in the MIDlet suite. |
MIDlet-Data-Size | No | The minimum number of bytes of persistent storage that the MIDlet requires. The default is zero. |
MIDlet-Description | No | A description of the MIDlet suite. |
MIDlet-Icon | No | The path name of a PNG file with in the JAR file to identify the MIDlet suite (not the individual MIDlets). It is used by the application management software to display an icon to identify the suite. |
MIDlet-Info-URL | Yes | A pointer to a URL containing a detailed description of the MIDlet suite. |
MIDlet-Name | Yes | The name of the MIDlet suite. |
MIDlet-Vendor | Yes | The name of the organization (or vendor) providing the suite. |
MIDlet-Version | Yes | The version number of the MIDlet suite presented in the format XX.YY.ZZ, where XX is the major, YY is the minor, and ZZ is the micro. If the micro is omitted, the default is zero. Therefore, the micro is optional. This information is used by the application management software for install and upgrade uses. |
Example 3-1 shows a sample manifest for a MIDlet suite (in this case, only two MIDlets) for a shopping MIDlet.
Example 3-1. A sample manifest
MIDlet-Name: ShopOnLine MIDlet-Version: 1.0 MIDlet-Vendor: SELKOM MIDlet-Description: a shopping MIDlet MIDlet-Info-URL: http://www.selkom.com/shop MIDlet-Data-Size: 500 MIDlet-1: BuyMIDlet, /icons/buy.png, com.selkom.BuyMIDlet MIDlet-2: PayMIDlet, /icons/sell.png, com.selkom.SellMIDlet MicroEdition-Profile: MIDP-1.0 MicroEdition-Configuration: CLDC-1.0
Java Application Descriptor (JAD)
Using a manifest to describe the MIDlets in the suite is a bit problematic. In the first section, we mentioned that before downloading a MIDlet or a MIDlet suite to a device, the Java Application Manager should check to make sure there is enough space for it. Using a manifest, however, means that the Java Application Manager should download the JAR file in order to read the manifest. Imagine downloading a MIDlet suite only to discover it cannot be installed on your device because it requires the next generation MIDP. To avoid these problems, the MIDP specification also defines the Java Application Descriptor (JAD).
A JAD file is a text file that is similar to a manifest. Unlike a manifest, however, it is not packaged in the JAR file. Similar to a manifest, it consists of a series of attributes used to describe a MIDlet suite. The possible attributes are shown in Table 3-7.
Table 3-7. JAD attributes
Attribute name | Required | Description |
---|---|---|
MIDlet-Name | Yes | The name of the MIDlet suite. |
MIDlet-Version | Yes | The version number of the MIDlet suite. The format is XX.YY or XX.YY.ZZ, where XX is the major, YY is the minor, and ZZ is the micro that is optional. If the micro is omitted, the default is zero. |
MIDlet-Vendor | Yes | The vendor of the MIDlet suite. |
MIDlet-Jar-URL | Yes | The URL from which to download the MIDlet suite. |
MIDlet-Jar-Size | Yes | The size of the MIDlet suite in bytes. |
MIDlet-Description | No | A description of the MIDlet suite. |
MIDlet-Icon | No | The pathname of a PNG file for the suite. The icon is used to identify the suite. |
MIDlet-Info-URL | No | A URL that describes the MIDlet suite in greater detail. |
MIDlet-Data-Size | No | The minimum number of bytes of persistent storage the MIDlet suite requires. If not specified, the default is zero. |
As you can see from Table 3-5 and Table 3-6, there are some common attributes between the manifest and the JAD file. The mandatory attributes that must be duplicated in both the manifest and the JAD file are: MIDlet-Name, MIDlet-Version, and MIDlet-Vendor.
Example 3-2 shows a JAD file for the same hypothetical MIDlet suite.
Example 3-2. A sample JAD file
MIDlet-Name: ShopOnLine MIDlet-Version: 1.0 MIDlet-Vendor: SELKOM MIDlet-Jar-URL: http://www.selkom.comlshoplmid.jar MIDlet-Jar-Size: 3544 MIDlet-Data-Size: 500
And that's the difference between a JAR manifest file and a JAD file in a MIDlet suite.
Before we start programming with MIDlets, let's briefly discuss some guidelines that are useful when developing applications for mobile information devices such as cell phones and PDAs that you likely haven't considered before.
When programming for mobile devices with a small memory footprint, it is crucial to make your applications run faster. The less time your application takes to run, the happier your customers will be. For the J2SE programmer, here are some ways to help you achieve the best performance:
for (int i=0; i<obj.getLength(); i++) { // do something with array elements }
where the length of the array is evaluated every time through the loop, it is much more efficient to do this:
int len = obj.getLength(); for (int i=0; i<len; i++) { // do something with array elements }
MIDlets are very simple to implement. All MIDlets must extend the javax.microedition.midlet.MIDlet abstract class and implement certain methods in that class. The MIDlet abstract class provides the basic functionality required in all MIDlets. A MIDlet runs in a controlled environment and therefore must implement certain methods that allow the application manager (which installs and runs the MIDlet) to control the behavior of the MIDlet. These methods are known as life cycle methods, since they reflect various states in which a MIDlet can be.
You'll recall from the previous section that a MIDlet can be in one of three states: paused, active, or destroyed. The state chart in Figure 4-1 shows the possible state transitions of a MIDlet, this time with the addition of the methods that the Java Manager will call inside the MIDlet code during those transitions.
Figure 4-l. MIDlet state transitions
Here, the javax.microedition.midlet.MIDlet abstract class defines three life cycle methods that are called during the state transitions: pauseApp(), startApp(), and destroyApp(). These three methods were present in the example we developed in the first section. The responsibilities for these three life cycle methods are as follows.
public void startApp()
This method indicates that the MIDlet is moving from a paused state to an active state. Here, the MIDlet will typically initialize any objects that are required while the MIDlet is active, and set the current display.
public void pauseApp()
This method is called when the MIDlet is moving from an active state to a paused state. This means that it will pause any threads that are currently active, as well as optionally setting the next display to be shown when the MIDlet is reactivated. Data can be persisted, if necessary, and retrieved later when the MIDlet is activated again.
public void destroyApp(boolean unconditional)
This method indicates that the MIDlet is moving to the destroyed state. It should free or close all resources that have been acquired during the life of the MIDlet. In addition, the method should persist any data that it wishes to save for future use.
It is important to note that startApp( ) can be called more than once. In addition to being called when the MIDlet is first activated to move the MIDlet from the paused state to the active state, it can also be called if the MIDlet has been paused during execution and wishes to again return to the active state.
The application manager, sometimes called the Application Management System (AMS) or MIDlet management software, is software that is preinstalled on a MIDP device and that functions as an operating environment. For example, on a Motorola i85s, the Java Apps menu item will start the application manager, which immediately shows the Java logo and the words "Mobile Information Device Profile Compatible" and then displays a menu of the MIDlet suites that have been installed on the phone.
However, the application manager must do more than simply show a menu of the MIDlet suites that are installed. According to the MIDP specification, the application manager must be able to:
As a MIDlet programmer, you typically won't need to be concerned with the internals of the application manager running on the device-it's unique to each device. However, some insight into its responsibilities is important when designing MIDP applications. In this case, the MIDlet life cycle methods can be called by the application manager to control the MIDlet state:
Example 4-1 shows a MIDlet skeleton class that implements the life cycle methods of the javax.microedition.midlet.MIDlet class.
Example 4-l. MIDlet skeleton
import javax.microedition.midlet.*; public class MyMIDlet extends MIDlet { public MyMIDlet() { // constructor } public void startApp() { // entering active state } public void pauseApp() { // entering paused state } public void destroyApp() { // entering destroyed state } }
Believe it or not, this class is all you need to create a MIDlet. The only thing we should reiterate is our earlier warning that startApp( ) can be called more than once.
Hence, if you have any one-time initialization that you wish to perform for your MIDlet, be sure that it is placed in the constructor of the MIDlet object and not in the startApp( ) method.
Earlier, we mentioned that a MIDlet could change its own state if needed. The javax.microedition.midlet.MIDlet abstract class provides three methods that can be called by a MIDlet to control its own state transitions:
public void notifyPause()
A MIDlet may call this method to pause itself. It can be called while in the active state, to inform the Java Application Manager that the MIDlet has entered the paused state.
public void resumeRequest()
A MIDlet may call this method to express interest in entering the active state. The application manager also calls this method to determine which MIDlet to activate, then it calls its startApp( ) method.
public void notifyDestroyed()
A MIDlet calls this method to destroy itself. It can be called while in the active state or the paused state, to indicate to the application manager that it has entered the destroyed state. Note that the application manager will not call destroyApp( ). Consequently, the MIDlet manages the release of its resources.
Now that you're familiar with MIDlet states and the application manager, let's create another MIDlet. As you've probably guessed by now, this involves the following five steps:
Let's review each of these steps. First, we'll look at the command-line technique that was shown in the first section. Then, we'll introduce the KToolbar application, which comes with the J2ME Wireless Toolkit and which can make our lives much easier.
The first step in the development life cycle is to write the MIDlet. Example 4-2 shows a simple MIDlet, PaymentMIDlet. This MIDlet creates a List object of type EXCLUSIVE (that is, only one option can be selected at a time), and adds three methods of payments to it. It displays a list of options for the user to select a method of payment.
Example 4-2. Sample MIDlet
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class PaymentMIDlet extends MIDlet { // The display for this MIDlet private Display display; // List to display payment methods List options = null; public PaymentMIDlet() { options = new List("Method of Payment", Choice.EXCLUSIVE); } public void startApp() { display = Display.getDisplay(this); options.append("Visa", null); options.append("MasterCard", null); options.append("Amex", null); display.setCurrent(options); } /** * Pause is a no-op since there are no background activities or * record stores that need to be closed. */ public void pauseApp() { } /** * Destroy must cleanup everything not handled by the garbage collector. * In this case there is nothing to cleanup. */ public void destroyApp(boolean unconditional) { } }
To compile the source code with the command-line tools of the Java Wireless Toolkit, use the javac command. Remember that you should use the -bootclasspath option to make sure the source code is compiled against the correct CLDC and MIDP classes.
C:\midlets> javac -bootclasspath C:\j2mewtk\lib\midpapi.zip PaymentMIDlet.java
This command produces the PaymentMidlet. class file in the current directory. This is a slightly simplified version of the command we used in the first section, which puts the resulting class file in a temporary directory.
The next step is to preverify the class file using the preverify command:
C:\j2mewtk\bin> preverify -classpath C:\midlets;C:\j2mewtk\lib\midpapi.zip PaymentMIDlet
Again, a slightly different approach. This command creates an output subdirectory in the current directory and writes a new file PaymentMIDlet.class. This is the preverified class that the KVM can run with its modified class verifier.
Package the Application in a JAR File
In order to enable dynamic downloading of MIDP applications, the application must be packaged in a JAR file. To create a JAR file, use the jar command:
C:\midlets> jar cvf payment.jar PaymentMidlet.class
A JAD file is necessary if you want to run a CLDC-compliant application.
Example 4-3 shows a sample JAD file for the payment MIDlet.
Example 4-3. A sample JAD file
MIDlet-1: payment,, PaymentMIDlet MIDlet-Name: Payment MIDlet-Version: 1.0 MIDlet-Vendor: ORA MIDlet-Jar-URL: payment.jar MIDlet-Jar-Size: 961
Once you have the JAD file, you can test your application using the MIDP emulator using the emulator command of the Java Wireless Toolkit, as shown here:
C:\j2mewtk\bin> emulator -Xdescriptor:C;\midlets\payment.jad
If all goes well, activate the MIDlet and you will see output similar to Figure 4-2.
If your MIDP application consists of multiple MIDlets, they can all be in one JAR file as a MIDlet suite. However, you would need to specify them in the JAD file using the MIDlet-n entry, where n is the number of the MIDlet. Consider the JAD file in Example 4-4, with three hypothetical MIDlets.
Figure 4-2. Running the payment MIDlet
Example 4-4. 7 demos MIDlets
MIDlet-1: Colors, /icons/ColorChooser.png, example.chooser.Color MIDlet-2: Properties, /icons/App.png, example.PropExample MIDlet-3: Http, , example.http.HttpTest MIDlet-4: FontTestlet, , example.fonts.FontTestlet MIDlet-5: Stock, /icons/Stock.png, example.stock.StockMIDlet MIDlet-6: Tickets, /icons/Auction.png, example.auction.NewTicketAuction MIDlet-7: ManyBalls, /icons/ManyBalls.png, example.manyballs.ManyBalls MIDlet-Description: Technical demonstration programs for MIDP MIDlet-Jar-Size: 104728 MIDlet-Jar-URL: demos.jar MIDlet-Name: SunSamples - Demos MIDlet-Permissions: javax.microedition.io.Connector.http,javax.microedition.io.Connector.https MIDlet-Vendor: Sun Microsystems, Inc. MIDlet-Version: 2.0 MicroEdition-Configuration: CLDC-1.0 MicroEdition-Profile: MIDP-2.0
If you run this JAD file, you would see something similar to Figure 4-3.
Figure 4-3. MIDlet suite
A MIDP application may consist of multiple MIDlets, as shown in Figure 4-3. Similarly, a desktop application consists of menus and options, as shown in Figure 4-4.
Figure 4-4. Desktop application
You have now seen how to compile, preverify, create JAR and JAD files, and run MIDlets from the command line. This is fine if you want to understand what's happening behind the scenes. However, there is an alternative. An integrated development environment, such as the J2ME Wireless Toolkit, can be used to simplify the development and deployment of MIDlets. The J2ME Wireless Toolkit comes with an application called KToolbar. The following steps show how to use the KToolbar to set up a simple MIDlet, develop the application, package it, and run it.
Figure 4-5. KToolbar screen
Figure 4-6. New project
Figure 4-7. Required attributes
Figure 4-8. Optional attributes
Now, use your favorite text editor and write PaymentMIDlet, or simply copy the source from Example 4-2. Then, save it in the required location and click on the Build button to compile it. Note that the KToolbar application performs all the steps of compiling the application, preverifying the classes, compressing them into a JAR file, and creating a corresponding JAD file for you. All you have to do is to click the Run button to run it. Then you can test your MIDlet using a default phone, Motorola's i85s, or a Palm OS, as shown in Figure 4-9.
Choose your favorite testing device to test the MIDlet. For example, Figure 4-10 shows the PaymentMIDlet running in a default gray phone device.
Figure 4-9. Select a testing device (upper right corner of KToolbar)
Figure 4-10. PaymentMIDlet on the default phone
Figure 4-11 shows the PaymentMIDlet running on Qwerty device.
Figure 4-11. PaymentMIDlet on QwertyDevice
As of this writing, deploying MIDlets is still an experimental process. However, the Java application manager that comes with the MIDP reference implementation now provides some clues about how we can deploy MIDlets to various devices.
Specifically, MIDlets can be installed in two ways:
The first method works well with PDAs, which are often used with a host computer, with which the PDAs frequently synchronize their data. For example, the MIDP for Palm implementation, is a good example of this; its application manager allows MIDlet suites to be installed from a host PC during the synchronization process.
The second method is more popular when installing MIDlets on cell phones and other wireless devices. With these devices, the most likely delivery transport is the wireless network itself. The process of deploying MIDlet suites over a network is referred to as over-the-air (OTA) provisioning. OTA provisioning is not yet part of the MIDP specification, but it is likely to become the dominant mechanism for distributing MIDlets and will probably be included in the formal specification soon.
As of this writing, OTA provisioning is just starting to be used with J2ME devices such as the Motorola i85s/i50x series of cell phones. OTA provisioning allows MIDlet providers to install their MIDlet suites via web servers that provide hypertext links. This allows you to download MIDlet suites to a cell phone via a WAP or Internet microbrowser. Here is a brief description of how this process works.
First, to deploy a MIDlet from a web server, you need to reconfigure your web server by adding a new MIME type:
text/vnd.sun.j2me.app-descriptor jad
How to add the MIME type depends on what server you are running. For example, if you're running Apache Tomcat, you would add a new MIME type by adding a new entry in the web.xml server configuration file, as follows:
<mime-mapping>
<extension>jad</extension>
<mime-type>text/vnd.sun.j2me.app-descriptor</mime-type>
</mime-mapping>
You would then use the following type of procedure to install a MIDlet suite from a web page:
<a href='MyApp.jad'>Click here to install the MIDlet suite</a>
Once the MIDlet is downloaded, it will be installed the first time you try to use it. A downloaded MIDlet stays on the device until you remove it (unlike Java applets).
Deploying to the Motorola i50x/i85s
You can also download J2ME applications to a Motorola/Nextel i50x or i85s device from your desktop through a data cable. This cable does not come with the phone itself, but can be ordered online from Nextel. The iDEN update software can then be downloaded from the iDEN development site ( http://www.motorola.com/idendev/, see Figure 4-12).
In addition, you can also purchase a data cable that comes with a CD-ROM containing the iDEN update software from Nextel from this site. Obtaining the software may involve authorization from your carrier, which can take between one and five days. Once you are granted authorization, however, you can install applications on up to five individual phones. The following paragraphs describe how to use the Motorola iDEN update software to download a J2ME MIDlet to your phone.
After you have obtained the update software, start it up and choose the J2ME Developers tab on the far left. From here, you can choose a JAD file to download the application into your phone through the data cable. Note that the JAD file and the JAR file must reside in the same directory and must have the same filename (excluding the extension).
For the most part, downloading an application to the phone is easy. However, the Motorola i85s and i50x phones will perform a number of checks to ensure the integrity of the application while installing it. You should observe the following rules to ensure that the phone will install the application.
The JAD file downloaded to the i85s or i50x must contain at least the following entries, which are case-sensitive:
MIDlet-Name: MIDlet-Version: MIDlet-Vendor: MIDlet-Jar-Size: MIDlet-Jar-URL:
It can also contain the following optional entries:
MIDlet-Description: MIDlet-Info-URL: MIDlet-Data-Size:
In addition, the JAD file can contain any other MIDlet-specific information that does not begin with the letters "MIDlet-".
Remember from the third section that the JAR file must contain a manifest with at least the following information, which must be identical to the data in the JAD file:
MIDlet-Name: MIDlet-Version: MIDlet-Vendor:
If you do not include this information in the manifest, the phone will respond with a "Descriptor Error" when it is attempting to install the application. If this happens, simply press the Menu button while the MIDlet is selected and remove it from the system.
Here are some other things to note when downloading to the Motorola i85s or i50x:
Figure 4-12. Motorola iDEN developer and update
Example 4-5 shows the manifest information that we would be using if we wanted to download the HelloMidlet application from the first section to the Motorola i85s.
Remember that the manifest must contain the three specified attributes (MIDlet-Name, MIDlet-Version, and MIDlet-Vendor) and that they must be identical to the values in the JAD file. If they differ, the phone will not install the MIDlet. We have also included the MIDlet class identification information and the profile and configuration version numbers, which we recommend that you include in your MIDlet manifests as well.
Example 4-5. Manifest.n2f
MIDlet-Name: HelloMidlet MIDlet-Vendor: ORA MIDlet-Version: 1.0.0 MIDlet-1: HelloMidlet" HelloMidlet MicroEdition-Profile: MIDP-1.0 MicroEdition-Configuration: CLDC-1.0
At this point, let's create a compressed JAR file of the classes that make up the MIDlet. With the manifest and the preverified class in the same directory, enter the following command:
>jar cvfm HelloMidlet.jar manifest.mf HelloMidlet.class
Once that is completed, you'll need to create the JAD file. Example 4-6 shows the JAD file for our HelloMidlet application. Note that we had to change the value of the MIDlet-Jar-Size attribute to match the size, in bytes, of the JAR file that we just created. In this case, it turned out to be 954 bytes with the additional manifest information.
Example 4-6. HelloMidlet.jad
MIDlet-1: HelloMidlet" HelloMidlet MIDlet-Jar-Size: 954 MIDlet-Jar-URL: http://www.oreilly.com/ MIDlet-Name: HelloMidlet MIDlet-Vendor: ORA MIDlet-Version: 1.0.0 MIDlet-Description: A sample application
Now we're ready to go. Again, be sure that the JAD file and the JAR file have the same name and reside in the same directory. Then use the iDEN software tools to download the application to your phone. It should only take a few seconds once you've chosen the target JAD file. After the download has completed, start the Java Application Manager on the phone (Java Apps under the Main Menu) and select the HelloMidlet application. Press the soft button to install it. You are now installing your first Java MIDlet on a real device. If everything goes okay, you can run your program after it completes the installation and verification steps.