Thursday, December 29, 2011

J2ME - Using Ant With J2ME

What is Ant
Apache Ant is an open source Java based build tool under the Apache Software License. Essentially Ant is to Java like make is to C/C++. It helps you to proficiently organize your code from build, test, deploy and execute. As well assist in multiple levels in debugging or optimization, custom packaging and is easily configurable when interfacing with 3rd party tools such as obfuscators. More infomation on Ant can be found at the Ant website, http://ant.apache.org.

Why Use Ant
Have you wondered if there is a better way to compile, debug, preverify, and deploy your code instead of manually re-entering all the long tedious commands at the command line. Yes, one can simply use batch file(s) but even then you are limited to what you can do. Batch files are okay if you do not plan on changing things. But we as developers know this isn't true. Another situation where batch files will fail you is say for some reason you now need to do your development in a Linux environment instead of a Windows environment. You would have to now migrate all you batch files to shell scripts. This would not be the case with Ant because it is XML compliant and is self contained making your Ant scripts platform independent.

Install Ant
Download Ant from http://ant.apache.org and install. It is fairly easy to install all you have to do is decompress it and set the system path the ant /bin directory.

Running Ant
Once you have properly installed Ant then test if the path is set correctly by simply going to command line and type in ant, assuming of course you are not in the bin directory where ant is installed. You should get an error message similar to the following:

C:>ant
Buildfile: build.xml does not exist!
Build failed
C:>

Do not worry everything is fine, it is looking for the build.xml file which we will go over next. At least now you now Ant is installed correctly and the path is set.

Usually Ant is ran with a command; for example, Ant compile. The command is defined in the build.xml file. Normally the build.xml file would have the following commands defined init, compile, deploy, preverify, and all. Where all would run through the first 4 defined commands.

Source Example - Hello World
Because the focus here is how to use Ant with J2ME we will stick with the standard HelloWorld midlet.
package hello;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class Hello extends MIDlet implements CommandListener {

private Command exitCommand; // The exit command
private Display display; // The display for this MIDlet

public Hello() {
display = Display.getDisplay(this);
exitCommand = new Command("Exit", Command.SCREEN, 2);
}

public void startApp() {
TextBox t = new TextBox("Hello MIDlet", "Test string", 256, 0);

t.addCommand(exitCommand);
t.setCommandListener(this);

display.setCurrent(t);
}


public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}

public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
}
}


build.xml
We will go through a basic build.xml and what is included in the HelloWorld build.xml file. Aside from following the Ant and XML standards there is no set way of making you build.xml file. You should build your build.xml file to fit your needs. You will notice this build script will include obfuscation, normally this would be an option, but most would agree in the mobile world obfuscation is mandatory because the limited amount of memory that is currently available on mobile handsets.

We will assume your directory structure is as follows before you run Ant, this what you need to setup manually.

/bin
/src
/res

After you run ant additional directories with subdirectories will be added, at the root of your build you will see the following:

/bin
/src
/res
/build
/deploy
/obf

Directory at Root Directory Contents Manually Created
/bin Where you would place the JAD and MANIFEST file Yes
/src Where you would place the source files, its not necessary to include the structure of the java packages, but you can if you like. The package structure should be automatically taken care of as long as the java source files properly define the package names Yes
/res Where you place resources such as graphics, maps, text files… etc. You will need to explicitly create the path to correctly represent the package layout defined in the java source files. Yes
/build Where you place resources such as graphics, maps, text files… etc. You will need to explicitly create the path to correctly represent the package layout defined in the java source files. No
/obf Where compile and preverify takes place No
/deploy The final result you would deploy to production No

Every build.xml file has a project tag. In this case we have also the default command set. If you simply enter the command Ant the default process will be trigged in this case clean. As well you need to indicate the base / root directory we are working from in this case we've set this to the current directory.

<project name="Hello" default="clean" basedir=".">
.
.
.
</project>

You next set of tags will be to set the global properties to make the build script more robust. This is where you will define path what jars to use and name mappings. For example say you using the obfuscator ProGuard version 1.4 and it was install on your c drive at c:proguard1.4.1 and you just recently upgraded to ProGuard version 1.5.1 and installed it under e:proguard1.5.1. All you would have to do is change the property of the path which ProGuard to use instead going through the entire script and change every occurrence of ProGuard. In this case the value of ProGuard is referenced by dolloar sign and open bracket the property name and closed braket, for example ${proguard}.

Another example of benefit when using Ant is that you will noticed we have defined the program name and package name. If you had another midlet called goodbye all you would have to do is change the project name, program name and package name. Of course you would have to set up a separate directory structure for the goodbye midlet but the build would essentially be the same. See how Ant can be a real time saver when start to make dozens and dozens of midlets.

<project name="Hello" default="clean" basedir=".">
<!-- set global properties for this build -->

<property name="program_name" value="Hello"/>
<property name="package_name" value="Hello"/>

<property name="proguard" value="e:/proguard1.5.1/lib/proguard.jar"/>
<property name="midp_home" value="e:/j2mewtk"/>
<property name="midp_lib" value="${midp_home}/lib/midpapi.zip"/>

<property name="top" value="."/>
<property name="src" value="src"/>
<property name="res" value="res"/>
<property name="build" value="build"/>
<property name="obf" value="obf"/>
<property name="deploy" value="deploy"

</project>

Now create the initialization tasks, clean is directory deletion and init is directory creation. Notice that the init task has an extra attribute in the opening tag depends, this ensures that whatever task is indicated in here is ran first before the init task is ran. So in this case it makes sure that the directories don't exist before it creates them. You will also notice both clean and init tasks use the global properties we defined earlier like ${deploy} for name of deploy directory. Of course you can add whatever you want in these two tasks.


<project name="Hello" default="clean" basedir=".">
<!-- set global properties for this build -->

<property name="program_name" value="Hello"/>
<property name="package_name" value="Hello"/>

<property name="proguard" value="e:/proguard1.5.1/lib/proguard.jar"/>
<property name="midp_home" value="e:/j2mewtk"/>
<property name="midp_lib" value="${midp_home}/lib/midpapi.zip"/>
<property name="top" value="."/>

<property name="src" value="src"/>
<property name="res" value="res"/>
<property name="build" value="build"/>
<property name="obf" value="obf"/>
<property name="deploy" value="deploy"
<target name="clean">
<!-- Delete our the ${build}, ${obf} and ${deploy} directory trees -->
<delete dir="${build}"/>
<delete dir="${deploy}"/>
<delete dir="${obf}"/>
</target>

<target name="init" depends="clean">
<!-- Create the build directory structure used by compile N deploy -->
<tstamp/>
<mkdir dir="${build}"/>
<mkdir dir="${deploy}"/>
<mkdir dir="${obf}"/>
</target>

</project>

Now add to the build.xml file the compile instructions. A classes directory is created in the build directory. After the java javac command is ran against the src directory and the class files are placed in the newly created classes directory. Of course, if you get errors you will have to correct these first to get a clean compile, Ant will let you know if there are compile problems.

<target name="compile">
<mkdir dir="${build}/classes"/>
<javac destdir="build/classes" srcdir="src" bootclasspath="${midp_lib}" target="1.1"/>
</target>

Now to preverify the classes, the preverified directory is made under the build directory. Notice the tags those are used for the arguments you would normal pass in during command line execution.

<target name="preverify">
<mkdir dir="${build}/preverified"/>
<exec executable="${midp_home}/bin/preverify">
<arg line="-classpath ${midp_lib}"/>
<arg line="-d ${build}/preverified"/>
<arg line="${build}/classes"/>
</exec>
</target>

Now define the task for code distribution. The class files, resource files and the MANIFEST file are bundled up into a jar file. Place both the jar file and jad file in the /build/bin directory.

<target name="dist">
<mkdir dir="${build}/bin"/>
<jar basedir="${build}/preverified"
jarfile="${build}/bin/${program_name}.jar"
manifest="bin/MANIFEST.MF">
<fileset dir="${top}/${res}">
<include name="${package_name}/*.png"/>
</fileset>
</jar>
<copy file="bin/${program_name}.jad" tofile="${build}/bin/${program_name}.jad"/>
</target>

Now obfuscate the JAR file to prevent re-engineering and to improve code efficiency. However, this step is a little more complicated because once you've obfuscated the jar you've essential changed the contents of the class files and the preverification you done earlier is no longer valid. To preverify the obfuscated classes we need to unjar the obfuscated jar to a temporary directory and preverify them one more time and package up a new jar once again.

The obfuscated classes are placed in the /obf directory and the repackaged jar is place in the /build/bin directory with the jad file. The original jar file is placed in the /build/bin/{name-of-jar}-orig/ directory

<!-- Obfuscate JAR -->
<target name="obfuscate">
<java fork="yes" classname="proguard.ProGuard" classpath="${proguard}">
<arg line="-libraryjars ${midp_lib}"/>
<arg line="-injars ${build}/bin/${program_name}.jar"/>
<arg line="-outjar ${obf}/${program_name}.jar"/>
<arg line="-keep 'public class * extends javax.microedition.midlet.MIDlet'"/>
</java>
<!-- UnJAR Obfuscated JAR -->
<unjar src="${top}/${obf}/${program_name}.jar" dest="${top}/${obf}/extract" />
</target>

<!-- Preverify the Obfuscated classes -->
<target name="preverifyobf">
<mkdir dir="${build}/preverifiedobf"/>
<exec executable="${midp_home}/bin/preverify">
<arg line="-classpath ${midp_lib}"/>
<arg line="-d ${build}/preverifiedobf"/>
<arg line="${obf}/extract"/>
</exec>
</target>

<!-- Re-Package a new Jar with the obfuscated classes -->
<target name="distobf">
<copy file="${build}/bin/${program_name}.jar" todir="${build}/bin/${program_name}-orig.jar"/>
<mkdir dir="${build}/bin"/>
<jar basedir="${build}/preverifiedobf"
jarfile="${build}/bin/${program_name}.jar"
manifest="bin/MANIFEST.MF">
<fileset dir="${top}/${res}">
<include name="${package_name}/*.png"/>
</fileset>
</jar>
<copy file="bin/${program_name}.jad" tofile="${build}/bin/${program_name}.jad"/>
</target>


Now the deploy! This is simply a copy of the final obfuscated jar and jad files from /build/bin to /deploy directory. You may want to modify this to deploy directly into the web server public space for OTA transmission.

<target name="deploy">
<copy file="${build}/bin/${program_name}.jad" tofile="${top}/${deploy}/${program_name}.jad"/>
<copy file="${build}/bin/${program_name}.jar" tofile="${top}/${deploy}/${program_name}.jar"/>
</target>

You can even get Ant to run the J2ME emulator, ensure that you have the correct path to the emulator set correctly and it is pointing to the right jad file.

<target name="run">
<exec executable="${midp_home}/bin/emulator">
<arg line="-Xdescriptor:${build}/bin/${program_name}.jad"/>
</exec>
</target>

Running More then One Task at a Time
When running Ant you can individually invoke the tasks, for example:
Ant clean
Ant compile
Ant preverify
Ant dist
Ant deploy
Ant run

To save you some time and typing you can define tags that run a set defined tasks, in this case we've defined a set of tasks without obfuscation and a set of tasks with obfuscation.

<target name="all-non-obf" depends="init,compile,preverify,dist"/>
<target name="all-obf" depends="init,compile,preverify,dist,obfuscate,preverifyobf,distobf"/>

So now all you have to type is:
Ant all-obf
Ant deploy
Ant run

You can simplify this even more by including the deploy and run into the all-obf task.

Summary
It may seem a bit painful at first to create the Ant build.xml file but in the long run it will save you lot more time and trouble. To run the HelloWorld with Ant make sure you have the following directory and file structure, at root 3 directories /bin, /res and /src with the build.xml file. In /bin you should have the Hello.jad file and MANIFEST.MF file. There will be nothing in the /res directory. The /src directory will of course of the Hello.java source file.

Here is the entire Ant build.xml file for the HelloWorld example.

<project name="Hello" default="clean" basedir=".">
<!-- set global properties for this build -->
<property name="program_name" value="Hello"/>
<property name="package_name" value="Hello"/>

<property name="proguard" value="e:/proguard1.5.1/lib/proguard.jar"/>
<property name="midp_home" value="e:/j2mewtk"/>
<property name="midp_lib" value="${midp_home}/lib/midpapi.zip"/>

<property name="top" value="."/>
<property name="src" value="src"/>
<property name="res" value="res"/>
<property name="build" value="build"/>
<property name="dist" value="dist"/>
<property name="obf" value="obf"/>
<property name="deploy" value="deploy"/>

<!-- Remove Build Directories -->
<target name="clean">
<!-- Delete our the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${deploy}"/>
<delete dir="${obf}"/>
</target>

<!-- Create Build Directories -->
<target name="init" depends="clean">
<!-- Create the build directory structure used by compile and dist -->
<tstamp/>
<mkdir dir="${build}"/>
<mkdir dir="${deploy}"/>
<mkdir dir="${obf}"/>
</target>


<!-- Compile Source -->
<target name="compile">
<mkdir dir="${build}/classes"/>
<javac destdir="build/classes" srcdir="src"
bootclasspath="${midp_lib}" target="1.1"/>
</target>

<!-- Preverify classes -->
<target name="preverify">
<mkdir dir="${build}/preverified"/>
<exec executable="${midp_home}/bin/preverify">
<arg line="-classpath ${midp_lib}"/>
<arg line="-d ${build}/preverified"/>
<arg line="${build}/classes"/>
</exec>
</target>
<!-- Package Preverifed classes, resources and MANIFEST file -->
<target name="dist">
<mkdir dir="${build}/bin"/>
<jar basedir="${build}/preverified"
jarfile="${build}/bin/${program_name}.jar"
manifest="bin/MANIFEST.MF">
<fileset dir="${top}/${res}">
<include name="${package_name}/*.png"/>
</fileset>
</jar>
<copy file="bin/${program_name}.jad" tofile="${build}/bin/${program_name}.jad"/>
</target>

<!-- Obfuscate unobfuscated jar file and unjar obfuscated jar to obf area -->
<target name="obfuscate">
<java fork="yes" classname="proguard.ProGuard" classpath="${proguard}">
<arg line="-libraryjars ${midp_lib}"/>
<arg line="-injars ${build}/bin/${program_name}.jar"/>
<arg line="-outjar ${obf}/${program_name}.jar"/>
<arg line="-keep 'public class * extends javax.microedition.midlet.MIDlet'"/>
</java>
<unjar src="${top}/${obf}/${program_name}.jar" dest="${top}/${obf}/extract" />
</target>

<!-- Preverify the obfuscated classes in the obf area -->
<target name="preverifyobf">
<mkdir dir="${build}/preverifiedobf"/>
<exec executable="${midp_home}/bin/preverify">
<arg line="-classpath ${midp_lib}"/>
<arg line="-d ${build}/preverifiedobf"/>
<arg line="${obf}/extract"/>
</exec>
</target>

<!-- Re-Package (Jar) the obfuscated classes, resources with MANIFEST file -->
<target name="distobf">
<copy file="${build}/bin/${program_name}.jar" todir="${build}/bin/${program_name}-orig.jar"/>
<mkdir dir="${build}/bin"/>
<jar basedir="${build}/preverifiedobf"
jarfile="${build}/bin/${program_name}.jar"
manifest="bin/MANIFEST.MF">
<fileset dir="${top}/${res}">
<include name="${package_name}/*.png"/>
</fileset>
</jar>
<copy file="bin/${program_name}.jad" tofile="${build}/bin/${program_name}.jad"/>
</target>

<!-- Deploy Final files from /build/bin ( can be either obfuscated or not obfuscated) -->
<target name="deploy">
<copy file="${build}/bin/${program_name}.jad" tofile="${top}/${deploy}/${program_name}.jad"/>
<copy file="${build}/bin/${program_name}.jar" tofile="${top}/${deploy}/${program_name}.jar"/>
</target>

<!-- Run final jar, currently set to /build/bin may want to change to /deploy -->
<target name="run">
<exec executable="${midp_home}/bin/emulator">
<arg line="-Xdescriptor:${build}/bin/${program_name}.jad"/>
</exec>
</target>

<!-- Combine Tasks -->
<target name="all-non-obf" depends="init,compile,preverify,dist"/>
<target name="all-obf" depends="init,compile,preverify,dist,obfuscate,preverifyobf,distobf"/>

</project>


This article first appeared at www3.telus.net

View All Articles by Jason Lam