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:
-
Get a copy of the Java Development Kit (a.k.a. JDK).
-
Write your classes.
-
Compile your classes using the JDK, possibly using Ant's
<javac> task.
-
Package your classes into a JAR file, possibly using Ant's
<jar> task.
-
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:
-
Get a copy of the Java Development Kit (a.k.a. JDK).
-
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.
-
Write your classes.
-
Compile your classes using the WTK, possibly using Ant's
<javac> task or Antenna's
<wtkbuild> task.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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:
-
Checks the .class file to ensure that there are
no native methods in it. Native methods are prohibited in the J2ME environment.
-
Checks the .class file to ensure that there is no
finalize method. Finalizers are prohibited in the J2ME
environment.
-
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.
-
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.
-
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.
|