AntME logo   AntME title
fill

A Brief J2ME Development Overview

This page is designed to give you a very brief overview of the differences between "standard" Java development and packaging, and the corresponding requirements under J2ME. This not a tutorial on J2ME programming - if you're looking for that, you can try looking here.

Standard Java Development and Packaging

Very loosely, the steps involved in doing standard Java development go something like:

  1. Get a copy of the Java Development Kit (a.k.a. JDK).
  2. Write your classes.
  3. Compile your classes using the JDK, possibly using Ant's <javac> task.
  4. Package your classes into a JAR file, possibly using Ant's <jar> task.
  5. Send your JAR file out into the world.

(I warned you this would be a very high level overview.)

J2ME Development and Packaging

J2ME development involves the same basic process as above, with a number of extra steps:

  1. Get a copy of the Java Development Kit (a.k.a. JDK).
  2. Get a copy of an appropriate Wireless Development Toolkit (WTK). Depending on what kind of development you're doing, this could be Sun's reference WTK, or it could be a handset vendor-specific WTK that contains additional functionality specific to that vendor.
  3. Write your classes.
  4. Compile your classes using the WTK, possibly using Ant's <javac> task or Antenna's <wtkbuild> task.
  5. Preverify your classes. This step is unique to J2ME, and is explained in more detail below. Antenna supports this operation via its <wtkbuild> task, or possibly implicitly as part of other Antenna tasks.
  6. Create a Java Application Descriptor (JAD) file. This is a text file which provides a handset with a variety of information about the application. A handset downloads this file first, and then determines later whether or not to actually download the JAR file.
  7. Package your classes into a JAR file, possibly using Antenna's <wtkpackage> task. During this process, information that is inside the JAD file also normally gets placed inside the JAR file's manifest.
  8. Update the JAD file to contain the size of the final JAR file. This is a mandatory field in the JAD file. Given that some of the JAD file contents need to go into the JAR file's manifest, this means that you usually have to create the JAD file, then create the JAR file, then go back and update the JAD file again once the JAR file's size is known.
  9. Sign your application. Many of the operations supported by MIDP-2.0 handsets require some degree of security - this security is part of Sun's MIDP-2.0 specification. In most "production" cases, you'll actually run your application through the JavaVerified process, since this certification is the one accepted by the majority of handsets. During development, however, it is common to use handsets that are configured to allow you to get the same effect by signing the application using a digital certificate obtained from Verisign, Thwaite or another similar provider.
  10. Post your JAD and JAR file on a web server so that people can download it. The URL you publish is the one to the JAD file. The JAD file, in turn, contains the URL to the JAR file. Since some handsets require an absolute URL to the JAR file, instead of allowing a relative URL, this means you frequently have to update your JAD file again.

As you can see, things are somewhat more involved. In particular, many J2ME emulators (which are part of the WTK's) require that you go through the whole preverify/package/JAD update cycle every time you want to fire up your application in the emulator.

Time for a shameless plug. If you happen to be a fan of the Eclipse development environment, which I am, I highly recommend that you look at the EclipseME plugin for doing J2ME development. This is a really, really nice piece of work by Craig Setera, which allows you to use all of the standard editing and debugging features of Eclipse, but targeting J2ME instead of standard Java. In particular, EclipseME allows you to work with a variety of different toolkits, and it handles all the grubby details of preverifying and packaging your classes and invoking the WTK's emulator. (In the interest of full disclosure, I'm a Junior Assistant Developer on the EclipseME project, although Craig has done the vast majority of the heavy lifting.)

Preverification

So what's the preverification stuff about?

When you fire up a standard Java application, the Java Runtime Environment (JRE) does a whole lot of checking on each and every class in your application as the classes are loaded. Basically, it walks through the entire .class file, examining all the opcodes, data, etc. to ensure that you don't do nasty things like trying to take the square root of a string, etc. All of this helps guarantee that even a hand-crafted .class file can't crash your computer. If only Microsoft's operating system were so secure. (Sorry, Redmond.)

Doing all this .class file verification is not a trivial operation. Your PC has a heck of a lot more processing power and memory than does your cell phone. So how does one get the same degree of safety on the cell phone as on your PC without it taking forever and using up tons of memory? The answer is that you require the developer to do some of the work up front. Specifically, the preverification step does the following:

  1. Checks the .class file to ensure that there are no native methods in it. Native methods are prohibited in the J2ME environment.
  2. Checks the .class file to ensure that there is no finalize method. Finalizers are prohibited in the J2ME environment.
  3. If you are building for the CLDC 1.0 environment, ensure that the class does not do any floating point operations. CLDC 1.0 does not support floating point. CLDC 1.0 does allow floating point, so this test depends on the environment for which you are building.
  4. Does some simplification of the internal code's exception processing. In standard Java, it is legal to implement certain kinds of exception processing using special "subroutines" that can be called, possibly recursively. Apparently this greatly complicates the class verification process, so it's prohibited in the J2ME environment. Since standard compilers generate this code, rather than write a custom compiler for the J2ME environment, Sun instead chose to fix this post-compile by using the preverification step.
  5. Finally, walks through class and generate stack maps. Java is a stack-oriented language, meaning that, for example, to add two numbers, you push the two on the stack, execute the add operator, and the result is to pop the two items off the stack, add them, and push the result back on the stack. As a result, a significant part of the security scan on the class involves making sure that the contents of the stack is correct for each operator. The preverifier handles part of this process up front by generating the stack maps and embedding them in the .class file. This cuts down on the amount of processing on the target device.

Thus, the preverifier does a combination of scanning a .class file for illegal stuff, as well as rewriting it to simplify the task of the target device. Since the class gets rewritten in this process, it is important that the preverification step be the last thing that modifies the .class files. In particular, if obfuscation or other class optimizations are part of your build process (like using ProGuard), preverification must be done after the obfuscation or optimization steps. If you don't do this, the stack maps are likely to be incorrect.