Learn details on how to develop BlackBerry application software using Apache Ant.
September 29, 2004
URL:http://www.drdobbs.com/blackberry-development-using-apache-ant/48800167
Research in Motion's (RIM) latest generation of Java-based BlackBerry
handsets supports the Mobile Information Device Profile (MIDP) 1.0
as well as an extensive set of BlackBerry-specific APIs, a CLDC-based
programming model, a proprietary development environment, unique
application deployment details, and a proprietary runtime/device
environment.
To help developers write BlackBerry applications, RIM offers the Java Development Environment (JDE), a BlackBerry-specific Integrated Development Environment (IDE) that supports various BlackBerry simulators for their various Java handsets. The JDE provides the typical features found on IDEs such as support for workspaces, projects, and the ability to build and debug BlackBerry applications all within the JDE.
However, you may need to build your application outside of the JDE, either because you prefer to work in a different IDE or because your BlackBerry application is just one part of a much larger system that needs to be built together. The JDE can generate a Makefile, but many developers prefer the cross-platform and extensible Apache Ant build tool.
In this article I will show how to use Ant to build BlackBerry applications. You can use this script to build from the command-line or your favorite IDE, and to integrate your application into your overall build process. I will begin with a brief introduction to the JDE's build capabilities, followed by how to use Apache Ant to build BlackBerry applications.
As previously mentioned, the JDE provides an integrated environment for writing, building and debugging your BlackBerry applications. The JDE can also generate a Makefile that you can use to build your application from the command line and/or integrate your application build into a Make-based development process. Figure 1 shows the "Generate Makefile and Resources" item on the Build menu.
Figure 1. - The JDE Build Menu
The result is a Makefile similar to the one in Listing 1. If you look at this Makefile
carefully, you will note a line similar to the following:
This line shows how to invoke the RAPC compiler that is used by the JDE to build the application's executable .cod file.
Lets briefly cover the files that you will encounter when creating a BlackBerry application:
It is important to emphasize that what gets loaded into BlackBerry handsets are .cod files. If you have a MIDlet, it must first be converted to a .cod package.
RAPC is the command line compiler that is used to compile .java files into .cod files that are loaded onto the handheld. Figure 2 illustrates the input and outputs of the RAPC compiler:
Figure 2. - Using the RAPC Compiler
Inputs to the RAPC compiler are:
For example, the RAPC compiler's usage is as follows:
rapc.exe import=C:\BlackBerryJDE3.7\lib\net_rim_api.jar codename=MyClientApp MyClientApp.jad MyClientApp.jar
The RAPC compiler generates a .cod file and a modified JAD file with BlackBerry specific entries added to it, such as the size of the .cod file, creation time, signature, and other. Listing 2 shows a JAD file for a BlackBerry CLDC application:
Listing 2. - Sample JAD file for a BlackBerry CLDC Application
Manifest-Version: 1.0 MIDlet-Version: 0.0 MIDlet-Jar-Size: 205354 MicroEdition-Configuration: CLDC-1.0 MIDlet-Jar-URL: MyClientApp.jar MIDlet-Name: MyClientApp MIDlet-1: ,, MicroEdition-Profile: MIDP-1.0 MIDlet-Vendor: J2MEDeveloper.com RIM-COD-Module-Dependencies: net_rim_cldc,net_rim_os RIM-MIDlet-Flags-1: 0 RIM-COD-Module-Name: MyClientApp RIM-COD-Size: 88616 RIM-COD-Creation-Time: 1089855050 RIM-MIDlet-Position-1: 0 RIM-COD-URL: MyClientApp.cod RIM-MIDlet-NameResourceId-1: 0 RIM-COD-SHA1: 89 bd de fe 1d 07 3a 0d 1a 15 23 ea 94 57 c5 fa 0f 2f 1f fe RIM-MIDlet-NameResourceBundle-1:
***Note that BlackBerry JDE and related build tools are only targeted at the Microsoft Windows NT, 2000 and XP environments.
There are plenty of great introductions to Ant, so here we will briefly cover the main concepts. Please refer to the resources section for a list of Ant resources.
Apache Ant is a build tool written in Java. An Ant script is an XML file that defines interdependent build tasks for a project such as "clean directories", "java compile", and "JAR classes". For our Ant scripts we leverage Antenna as much as possible. Antenna is a set of Ant tasks for building wireless Java applications targeted at MIDP; this simplifies many Ant script tasks which otherwise we would have to write ourselves.
A proper directory structure for your project will help you maintain your project's files neatly organized. Figure 3 illustrates a project organization that I have found useful; this directory structure is used by the finished Ant script I'll present below:
Figure 3. Our Project Directory Structure
The project's root directory contains the build.xml file as well as other support files such as our JAD and MANIFEST template files - we use these templates to generate our final (properly) populated JAD and MANIFEST files. The rest of our directories are for our compiled classes, build output, the source code, resource files, and support libraries.
Our Ant script was written following the Makefile in Listing 1. Our script provides build tasks to clean directories, and build our application. Our Ant script consist of the following major tasks:
Separating the build into these steps allows us to easily create both a JAD/JAR and COD files. Note how the common build tasks generate a MANIFEST, JAD, and JAR files, and how the generated JAD and JAR files are used as input into the BlackBerry-specific build tasks.
Figure 4. Ant Script Build Tasks
Listing 3 shows the finished Ant script:
Listing 3. The Ant Script
<?xml version="1.0"?><project name="MyClientApp" default="build" basedir=".">
<property name="ver" value="1.0"/> <property name="codename" value="MyClientApp"/>
<property name="midp_lib" location="${j2mewtk.home}/lib/midpapi.zip"/>
<property name="build.jars" location="lib/thirdparty"/> <property name="bbjdebuild.jars" location="${build.jars}/jde3.7"/> <property name="net_rim_api.jar" value="${bbjdebuild.jars}/net_rim_api.jar"/>
<property name="src" location="src"/> <property name="resources" location="res"/> <property name="lib" location="lib"/>
<property name="unpreverified.classes" value="classes/unpreverified"/> <property name="obfuscated.classes" value="classes/obfuscated"/> <property name="final.classes" value="classes/final"/>
<property name="name" value="${codename}"/> <property name="jad.template" value="worktrack.jad.template"/> <property name="jadfile" value="output/tojar/${name}.jad"/> <property name="jarfile" value="output/tojar/${name}.jar"/>
<property name="manifest.template" value="manifest.template"/> <property name="manifestfile" value="MANIFEST.MF"/>
<property name="temp.jar" value="output/tojar/${name}_t.jar"/> <property name="obfuscated.jar" value="output/tojar/${name}_t_o.jar"/> <property name="preverified.jar" value="output/tojar/${name}_p.jar"/> <property name="final.jar" value="output/tojar/${name}.jar"/>
<property name="wtk.home" value="${j2mewtk.home}"/> <property name="antenna.jar" value="antenna-bin-0.9.12.jar"/> <taskdef name="wtkbuild" classname="de.pleumann.antenna.WtkBuild" classpath="${build.jars}/${antenna.jar}"/>
<taskdef name="wtkpreverify" classname="de.pleumann.antenna.WtkPreverify" classpath="${build.jars}/${antenna.jar}"/>
<taskdef name="wtkpackage" classname="de.pleumann.antenna.WtkPackage" classpath="${build.jars}/${antenna.jar}"/>
<target name="init" depends="clean"> <mkdir dir="classes"/> <mkdir dir="${unpreverified.classes}"/> <mkdir dir="${obfuscated.classes}"/> <mkdir dir="${final.classes}"/> <mkdir dir="output"/> <mkdir dir="output/tojar"/> <mkdir dir="output/tocod"/> </target>
<target name="clean"> <delete file="${name}.jad"/> <delete file="${name}.jar"/> <delete file="${codename}.cod"/> <delete file="${codename}.lst"/> <delete file="${codename}.debug"/> <delete file="${codename}.csl"/> <delete file="${codename}.cso"/> <delete dir="classes"/> <delete dir="output"/> </target> <!-- Preverify the compiled code --> <target name="javacompile"> <wtkbuild srcdir="${src}" destdir="${unpreverified.classes}" bootclasspath="${net_rim_api.jar}"/> </target>
<!-- Preverify the compiled code --> <target name="preverify" depends="javacompile"> <wtkpreverify srcdir="${unpreverified.classes}" destdir="${final.classes}" classpath="${net_rim_api.jar}"/> </target>
<!-- Version the JAD and MANIFEST Files --> <target name="version" depends="preverify"> <!-- Version the MANIFEST --> <filter token="buildVer" value="${ver}" /> <filter token="midletName" value="${name}" /> <copy file="${manifest.template}" tofile="${manifestfile}" filtering="true" overwrite="true" />
<!-- Version the JAD file --> <filter token="buildVer" value="${ver}" /> <filter token="midletName" value="${name}" /> <filter token="jarName" value="${name}.jar" /> <copy file="${jad.template}" tofile="${jadfile}" filtering="true" overwrite="true" /> </target>
<!-- Package (JAR) the compiled classes. This also modifies the JAD file with JAR size information --> <target name="package" depends="version"> <delete dir="${final.classes}/META-INF" /> <wtkpackage jarfile="${final.jar}" jadfile="${jadfile}"> <fileset dir="${final.classes}"/> <fileset dir="${resources}"/> </wtkpackage> </target>
<!-- Invoke the RAPC compiler. This step is based the RAPC's usage: rapc.exe import=RIM_APIs codename=Codename JAD-File JAR-File --> <target name="rapc" depends="package" description="RIM COD Compiler"> <exec dir="." executable="${bbjdebuild.jars}/rapc.exe"> <arg line=" import=${net_rim_api.jar} "/> <arg line=" codename=${codename} "/> <arg line=" ${jadfile} "/> <arg line=" ${jarfile} "/> </exec> </target>
<!-- Once the COD file has been generated, move generated file to output directory --> <target name="build" depends="rapc"> <move file="${codename}.cod" tofile="output/tocod/${codename}.cod"/> <move file="${codename}.debug" tofile="output/tocod/${codename}.debug"/> <move file="${codename}.cso" tofile="output/tocod/${codename}.cso"/> <copy file="${codename}.alx" tofile="output/tocod/${codename}.alx"/> <copy file="${jadfile}" tofile="output/tocod/${codename}.jad"/> </target>
</project>
If you look at the rapc
Ant target task in Listing 3, you will see it was written based
on the RAPC compiler usage format. But please note that starting with Antenna
version 0.9.13,
support for RAPC compilation (contributed by me) is now available. To use the
new RACP Ant support first
download the latest version of Antenna, then
add to your build.xml script the following task definition, similarly to how we
defined the rest of the Antenna Ant tasks:
<taskdef name="wtkrapc" classname="de.pleumann.antenna.WtkRapc" classpath="${build.jars}/${antenna.jar}"/>
The following snippet shows how to use the new Antenna RACP task (feel free to replace the racp
target
in Listing 3 with the following code snippet). For more information please refer
to the Antenna Javadoc:
<target name="rapc" description="RIM COD Compiler" depends="package"> <wtkrapc quiet="true" midlet="false" jadfile="${jadfile}" source="${jarfile}" codename="${codename}" import="${bb.api.jar}" destdir="output/tocod/"/> </target>
If you look at the version
Ant target task,
you will notice that it preprocesses the JAD and MANIFEST files,
adding version information. Listings 4 and 5 show the template
files, and their final state after versioning and packaging.
Listing 4a. Original JAD Template File
MIDlet-Name: @midletName@ MIDlet-Version: @buildVer@ MIDlet-Vendor: J2MEDeveloper MIDlet-Description: MicroEdition-Profile: MIDP-1.0 MicroEdition-Configuration: CLDC-1.0 MIDlet-Jar-URL: @jarName@ MIDlet-Jar-Size: @jarSize@ MIDlet-1: ,,
Listing 4b. Final JAD File - Versioned and with BlackBerry-specific Information
Manifest-Version: 1.0 MIDlet-Jar-Size: 160220 MIDlet-1: ,, MIDlet-Jar-URL: MyAppClient.jar MicroEdition-Configuration: CLDC-1.0 MIDlet-Version: 1.0 MIDlet-Name: MyAppClientMIDlet-Description: MIDlet-Vendor: J2MEDeveloper MicroEdition-Profile: MIDP-1.0 RIM-COD-Module-Name: MyAppClient RIM-COD-Module-Dependencies: net_rim_cldc,net_rim_os RIM-COD-Creation-Time: 1092667276 RIM-COD-URL: MyAppClient.cod RIM-COD-SHA1: a1 82 6f e6 1a 62 80 b8 8b 63 6c 54 69 11 0b 4b 63 78 05 84 RIM-COD-Size: 92928
Listing 5a. Original MANIFEST Template File
MIDlet-Name: @midletName@ MIDlet-Version: @buildVer@ MIDlet-Vendor: J2MEDeveloper MIDlet-Description: MicroEdition-Configuration: CLDC-1.0 MicroEdition-Profile: MIDP-1.0 MIDlet-1: , ,
Listing 5b. Final Versioned MANIFEST File
Manifest-Version: 1.0 Ant-Version: Apache Ant 1.6.1 Created-By: 1.4.2-b28 (Sun Microsystems Inc.) MIDlet-Name: MyAppClient MIDlet-Version: 1.0 MIDlet-Vendor: J2MEDeveloper MIDlet-Description: MicroEdition-Configuration: CLDC-1.0 MicroEdition-Profile: MIDP-1.0 MIDlet-1: , ,
Running our application in the simulator requires us to first copy the generated files into the simulator directory, then invoke the appropriate simulator with the appropriate arguments. For this we will add two more steps to our Ant script:
The simulator to invoke will depend on which simulators you have installed on
your computer. To write our run build task for a particular handset the easiest thing to do is to open the appropriate batch file for the simulator
for the handset of interest (found in the simulator directory), and
just transfer the input arguments into the Ant task. For example, for the the
7500 handset we would open the OS7500.bat batch file. We will see
that the batch file invokes the osloader.exe
passing
the os7500.dll as one of its argument. The usage of the osloader.exe command is a follows:
osloader.exe Os7500.dll /wi /app:DisableRegistration /rsim=0x20000001 /F16384
/H1119264 /GRES=240x160x16 /rport=19780 /rport=8205 jvm.dll
If you look at Listing 6 below, you will see how our run
Ant task mirrors the line
above:
Listing 6. Ant Tasks to Run Your Application
<target name="updatesim" description="Update BlackBerry Simulator"> <copy todir="${blackberry.simulator.path}/"> <fileset dir="output/tocod"/> </copy> </target><!-- The following same arguments were in the OS7500.bat file: osloader.exe Os7500.dll /wi /app:DisableRegistration /rsim=0x20000001 /F16384 /H1119264 /GRES=240x160x16 /rport=19780 /rport=8205 jvm.dll --> <target name="run" description="Run"> <exec dir="${blackberry.simulator.path}" executable="${blackberry.simulator.path}/osloader.exe"> <arg line=" Os7500.dll /wi " /> <arg line=" /wi " /> <arg line=" /app:DisableRegistration " /> <arg line=" /rsim=0x20000001 " /> <arg line=" /F16384 " /> <arg line=" /H1119264 " /> <arg line=" /GRES=240x160x16 " /> <arg line=" /rport=19780 " /> <arg line=" /rport=8205 " /> <arg line=" jvm.dll " /> </exec> </target>
In this article we have covered the JDE's build capabilities. We also covered Ant, its benefits, and how to use if for your BlackBerry build process. Ant is a very powerful build tool, and with this article you should now have enough information to support Ant for all your BlackBerry builds, build your application from IDEs such as IDEA, Eclipse and Netbeans, and integrate your BlackBerry build into your overall build process.
C. Enrique Ortiz is a software architect and developer, and a mobile/wireless technologist and writer. He is author or co-author of many publications, a co-designer of Sun Microsystems' the Mobile Java Developer Certification Exam, and has been an active participant in the wireless Java community and of various J2ME expert groups.
Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.