July 28, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Getting Started with MIDlets and the Sun Java Wireless Toolkit for CLDC

  • January 2, 2008
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming Notes # 2570


Preface

This is the first lesson in a series of tutorial lessons designed to teach you how to write programs using the Sun Java Wireless Toolkit for CLDC (Connected Limited Device Configuration.) According to Sun (see Resources):

"The Sun Java Wireless Toolkit (formerly known as J2ME Wireless Toolkit) is a set of tools for creating Java applications that run on devices compliant with the Java Technology for the Wireless Industry (JTWI, JSR 185) specification and the Mobile Service Architecture (MSA, JSR 248) specification."

Stated more simply, the toolkit is used to create Java programs capable of running on small devices having limited resources such as cell phones.

What is a MIDlet?

In this series of lessons, I will start from scratch and help you work your way up to the point where you can write simple games and produce simple multimedia on cell phones. Such a program is often called a MIDlet. According to Wikipedia (see Resources):

"A MIDlet is a Java program for embedded devices, more specifically the Java ME virtual machine. Generally, these are games and applications that run on a cell phone."

According to the SCMAD Certification Center (see Resources):

"A MIDlet is an application written for MIDP. MIDlet applications are subclasses of the javax.microedition.midlet.MIDlet class that is defined by MIDP."

In this first lesson, I will present some general background information about MIDlets and provide you with a Java programming framework that makes it easy to experiment with the programming of MIDlets.

General

The following steps are required to produce a MIDlet and to get it running in a cell phone:

  1. Design the MIDlet (similar to designing any Java program).
  2. Write the program code (similar to coding any Java program).
  3. Compile the code (targeted to a Java v1.4 virtual machine, a specific version of the CLDC, and a specific version of the MIDP).
  4. Pre-verify the compiled code.
  5. Create a manifest file.
  6. Create a Java ARchive (JAR) file.
  7. Create a Java Application Descriptor (JAD) file.
  8. Test the MIDlet in a cell phone emulator.
  9. Deploy the MIDlet into a cell phone.

The Java framework program that I will present in this lesson handles items 3 through 8 in the above list with the single click of a button. I will discuss items 1 and 2 in some detail in subsequent lessons.

The final product - a MIDlet suite

 
Other file types
Future lessons will update the MIDlet-programming framework to accommodate image files and other specialized file types as well.

In the final analysis, only the JAR file and the JAD file are required to deploy the MIDlet into a cell phone. Together these two are called a MIDlet suite (see MIDlet Life Cycle in Resources). Therefore, the MIDlet-programming framework program that I will present cleans up after itself, automatically deleting all of the extraneous files that are created during the MIDlet development process, leaving only the source code files, the JAR file, and the JAD file.

I will leave it up to you to research the web and learn how to deploy a MIDlet into your particular brand and model number cell phone (see item 9). There is a wealth of information available on that topic on various web sites.

Acknowledgement

I would be remiss if I failed to mention the articles published by Vikram Goyal and others on this topic (see Resources). It was those articles that got me started down the path to learning about MIDlet programming.

Viewing tip

I recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures and listings while you are reading about them.

Figures

  • Figure 1. User interface for the framework program.
  • Figure 2. Sun's cell phone emulator program.
  • Figure 3. Usage instructions for the preverify method.
  • Figure 4. Required contents for the manifest file.
  • Figure 5. Minimum requirements for a JAD file.
  • Figure 6. Typical screen output from deleteOldStuff method.
  • Figure 7. Typical output from the compile process.
  • Figure 8. Typical screen output for preverification process.
  • Figure 9. Screen output from deleting class files.
  • Figure 10. Screen output from moving files.
  • Figure 11. The screen output from making the JAR file.
  • Figure 12. The screen output for the cleanup phase.
  • Figure 13. The cell phone emulator with the MIDlet loaded.
  • Figure 14. The cell phone emulator with the MIDlet running.
  • Figure 15. Screen output produced by the runEmulator method.

Listings

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials. You will find a consolidated index at www.DickBaldwin.com.

General background information

Design and coding

In an earlier section, I provided a list of nine steps that are required to get a MIDlet up and running in a cell phone. The first two steps are similar to the design and coding steps required for the writing of any Java program. Of course, when designing and coding a MIDlet, you must be cognizant of the fact that a cell phone has very limited resources as compared to a modern desktop or laptop computer. According to Goyal (see Resources):

"The most popular profile and configuration that Sun provides are the Mobile Information Device Profile (MIDP) and Connected Limited Device Configuration (CLDC), respectively. As the name suggests, CLDC is for devices with limited configurations; for example, devices that have only 128 to 512KB of memory available for Java applications. Consequently, the JVM that it provides is very limited and supports only a small number of traditional Java classes. (This limited JVM is actually called the KVM.)"

Also, as anyone who has ever used a cell phone is aware, the size of the screen in pixels is generally much smaller than the size of the screen on a typical desktop or laptop computer.

Compiling the code

As you will see later, you can use Sun's standard javac.exe program to compile your MIDlet. However, you must make adjustments so that the compiler will use the classes in the wireless toolkit library in place of the classes in the standard J2SE library.

When using version 2.5.2 of the wireless toolkit, you have your choice of two different CLDC libraries and three different MIDP libraries. The choice of which you should use depends on the brands and model numbers of the targeted cell phones. Different cell phones accommodate different programming features. The different libraries in the toolkit provide those different features. Sun provides The Java ME Device Table (see Resources) to help you decide which libraries you should target when compiling your MIDlet.

User interface for the framework program

Figure 1 shows a screen shot of the user interface for the Java WTK framework program that I will provide in this lesson.

Figure 1. User interface for the framework program.

As you can see from Figure 1, this framework program makes it easy for you to select a specific CLDC and a specific MIDP during the compilation process simply by selecting two radio buttons before clicking the Run button. (Even these selections have the default values shown in Figure 1, so if you are satisfied with those selections, you can simply enter the name of your program and click the Run button.)

Which Java version should you use?

Here is a quotation from Goyal's article (see Resources):

"Warning: I had problems getting the Wireless Toolkit to work properly with JDK 5.0. If you don't need the latest features in version 5.0, it is best to stick to any 1.4.2 version."

I have independently and experimentally confirmed that Sun's WTK2.5.2 also won't work properly with Sun's Java standard edition v1.6. Rather, it is necessary to compile the MIDlet code in such a way as to make it compatible with Sun's Java virtual machine v1.4. I will show you how to do that later when I discuss the code in my framework program.

The pre-verification step

Referring back to the earlier list, we see that when grouped together, the remaining steps following compilation consist of:

  • Pre-verifying the compiled MIDlet code
  • Packaging the pre-verified MIDlet code in a JAR file with an accompanying JAD file
  • Testing the MIDlet
  • Deploying the MIDlet

Goyal explains the purpose of the pre-verification step quite well (see Resources), so I won't repeat that information in this lesson. Suffice it to say that this step involves executing a WTK2.5.2 program named preverify.exe (with the proper command-line parameters) to transform the collection of class files produced by the compiler into a collection of class files that will execute more efficiently in the cell phone.

Packaging the MIDlet for testing and deployment

Following the pre-verification step, packaging the MIDlet for testing and deployment consists of the following three individual steps:

  • Create a manifest file.
  • Encapsulate the manifest file and the pre-verified class files in a JAR file.
  • Create a JAD file that describes the JAR file.

These three steps are rather mechanical. I will explain how to accomplish them later when we examine the code in my framework program.

Testing the MIDlet

Fortunately, the WTK2.5.2 contains a cell phone emulator program that can be used to test your MIDlet before committing it to actual cell phone hardware. Figure 2 shows a screen shot of the emulator running a simple MIDlet that displays my name on the screen.

Figure 2. Sun's cell phone emulator program.

I will show you how to use the emulator to test your MIDlet later in the detailed discussion of my framework program.

Deploying the MIDlet

Once again, Goyal provides a good discussion of the various options that are available for deploying your MIDlet onto actual cell phone hardware, so I won't duplicate that discussion in this series of lessons.

Preview

I plan to accomplish the following tasks in this lesson:

  1. Teach you how to develop and test a MIDlet by executing a series of batch command files.
  2. Demonstrate that WTK2.5.2 supports both local classes and member classes in a MIDlet.
  3. Teach you about my framework program for the development and testing of MIDlets.
  4. Teach you how to use my framework program for the development and testing of MIDlets.

Four ways to develop and test a MIDlet

I know of at least four ways that you can develop and test a MIDlet program:

  1. By entering a series of tedious and complex commands at the command line.
  2. By executing a series of batch files that essentially encapsulate those commands.
  3. By running the Wireless Toolkit program.
  4. By running the framework program that I will provide in this lesson.

Executing commands at the command line

Goyal shows you how to do this in his first article (see Resources). While the process isn't complex, it is extremely tedious and prone to problems resulting from typing errors. Therefore, I don't recommend this approach.

Executing batch files

In effect, the batch files contain the same commands that you would execute at the command line if you were to take that approach. While the use of batch files is not ideal, it is much less tedious than executing commands at the command line. At least when you use batch files, you can gracefully correct your typing errors, and the process is entirely repeatable.

There are some good aspects to using batch files, particularly with respect to understanding what is going on. I will present and explain six batch files and an associated text file that you can customize for your own use.

Running the Wireless Toolkit program

Once you have become proficient in programming MIDlets and start doing such programming in a serious way, this may be your best approach. However, it has some problems, which in my opinion, diminish its value during the learning phase.

Java version compatibility

Perhaps the most important problem with the Wireless Toolkit program has to do with a compatibility issue between the toolkit program and versions of J2SE beyond v1.4. Cell phones capable of executing MIDlets contain a small Java Virtual Machine. Apparently the virtual machine in the WTK2.5.2 emulator is not compatible with the bytecode produced by J2SE v1.5 and J2SE v1.6. I will show you how to get around that problem for batch files and also for my framework program. However, short of actually having v1.4 installed on my computer, I haven't figured out how to get around the problem using the Wireless Toolkit program. Since I am currently running v1.6, and I teach courses that require the features introduced in v1.5 and v1.6, I don't want to install v1.4 on my computer.

Lots of leftover garbage

Another problem that I have with the Wireless Toolkit program for experimental work is the large number of folders and files that it creates and leaves hanging around on the disk whenever you use it. I am very frugal when it comes to the use of disk space, not because of a shortage of disk space, but because of the backup problems that are caused by having a lot of garbage on the disk.

As I have mentioned earlier, in most cases my framework program will clean up after itself, leaving only the source code, a JAR file, and a JAD file on the disk. Only the JAR file and the JAD file are required for testing and deployment of the MIDlet in a cell phone.

Running my framework program

Obviously, this is my preference. Otherwise, I wouldn't have invested the time and effort required to develop and test the framework. I will be teaching you all about the framework in this and subsequent lessons.

Local classes and member classes

I am a heavy user of member classes, local classes, and anonymous classes. Therefore, one of my objectives was to demonstrate that WTK2.5.2 supports both local classes and member classes in a MIDlet. For that reason, I will present and briefly explain a very simple MIDlet program named WTK001 that consists of one top-level class, one local class, and one member class.

The behavior of the MIDlet is very simple. It displays my name (or your name if you prefer) on the cell phone screen. The point isn't the behavior of the MIDlet. Rather the point is the class structure that is used to organize the MIDlet. A method in an object of the top-level class calls a method in an object of the local class, which in turn gets and returns the value of an instance variable in an object of the member class. Thus, the three objects are inextricably linked to one another.

Let's see some code

That's enough talk.  Let's see some code.

Discussion and sample code

The MIDlet program named WTK001

I will begin by providing a brief introduction to the MIDlet program named WTK001. A complete listing of this program is presented in Listing 42 near the end of the lesson. I don't want you to worry too much about the actual Java code in the program just yet. I will explain that code in a future lesson. In this lesson, I want you to concentrate on the class structure used to create the program.

Linked objects from three classes

The purpose of this program is to illustrate a MIDlet that is composed of linked objects from three classes. One of the classes is a top-level class, one is a local class, and one is a member class. A method in an object of the top-level class calls a method in an object of the local class, which in turn gets and returns the value of an instance variable in an object of the member class. That value, which is my name, is ultimately displayed on the cell phone screen.

The output class files

The compilation of this program produces the following three class files:

  • WTK001$1LocalClass.class
  • WTK001$MemberClass.class
  • WTK001.class

If you are familiar with member classes and local classes, you should recognize two of these class file names as being in those categories.

The MIDlet was tested using a Java SE 6 compiler, targeted at a V1.4 virtual machine, and WTK 2.5.2 running under Windows XP.

Discuss the program in fragments

As is my custom, I will present and discuss the programs in this lesson in fragments.  The top-level class begins in Listing 1, which also shows the beginning of the constructor for that class.

Listing 1. Beginning of the top-level class for WTK001.

package WTK001;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;

public class WTK001 extends MIDlet{

  Alert alertDisplay;

  public WTK001(){//constructor
    alertDisplay = new Alert("Name Display, WTK001");
    alertDisplay.setTimeout(10000);

    //==================================================//
    //This is a local class. The method named getName
    // instantiates a member class, gets the value of a
    // public instance variable from that object, and
    // returns that value.
    class LocalClass{
      public String getName(){
        return new MemberClass().name;
      }//end getName
    }//end class LocalClass
    //==================================================//

The definition of a local class

For purposes of the intent of this lesson, one of the most important aspects of the code in Listing 1 is the definition of the small local class named LocalClass early in the constructor. Note that the instance method named getName belonging to an object of the local class instantiates an object of a member class named MemberClass, gets the value of an instance variable named name belonging to that object, and returns that value.

The code in Listing 2 instantiates an object of LocalClass discussed above, calling the method named getName on that object. The value returned by the getName method is stored in a location from which it can later be displayed on the cell phone screen.

Listing 2. Instantiate an object of LocalClass.

    alertDisplay.setString(new LocalClass().getName());
  }//end constructor

Listing 2 also signals the end of the constructor for WTK001.

Inherited abstract methods

Listing 3 begins with three methods that we aren't going to worry much about in this lesson because I will be discussing them in detail in future lessons.

Listing 3. Inherited abstract methods.

  public void startApp(){
    Display.getDisplay(this).setCurrent(alertDisplay);
  }//end startApp

  public void pauseApp(){
  }//end pauseApp

  public void destroyApp(boolean unconditional){
  }//end destroyApp

Suffice it to say at this point that these three methods are inherited from the MIDlet class where they are declared as abstract methods. Therefore, the WTK001 class must provide concrete definitions for the three methods or the class must be declared abstract.

As you can see, two of the three methods in Listing 3 are defined simply as empty methods in this class. The method named startApp causes my name to be displayed on the cell phone screen as shown in Figure 2.

The member class named MemberClass

Listing 4 shows the definition of the member class named MemberClass. (Note that this entire class is defined inside of the class named WTK001.)

Listing 4. The member class named MemberClass.

  class MemberClass{
    public String name = "Dick Baldwin";
  }//end class MemberClass
  //====================================================//
  
}//end class WTK001

This is a very simple class containing a single instance variable populated with my name. This is the value that is displayed on the cell phone screen in Figure 2.

The batch file approach

Earlier in this lesson, I provided a list of nine steps that are required to produce a MIDlet and to get it running in a cell phone. In this section of the lesson, I will present and explain six batch files and one text file that can be used to accomplish steps 3 through 8 in that list.

The compilation batch file

A batch file that can be used to compile the MIDlet program named WTK001 is shown in Listing 5.

Listing 5. The compilation batch file named 1-Compile.bat.

rem Note:  It is necessary to target Java v1.4 for
compatibility with the J2ME library classes.

rem Set path to root of wireless toolkit directory tree.
set WTK2.5.2=M:\WTK2.5.2

javac 
-target 1.4 
-source 1.4 
-bootclasspath 
%WTK2.5.2%\lib\cldcapi11.jar;
%WTK2.5.2%\lib\midpapi20.jar 
WTK001\WTK001.java

dir WTK001\*.class

pause

Disk directory structure

Before getting into a detailed discussion of the batch file shown in Listing 5, we need to take care of some housekeeping chores. The first housekeeping chore that I will explain is the disk directory structure.

The batch file named 1-Compile.bat shown in Listing 5 was executed from a directory named Examples on my computer. (You can name that directory anything you like.) One of the subdirectories of the Examplesdirectory is a directory named WTK001. (You need to stick with this name unless you plan to modify the batch files.) The source code file named WTK001.java, (shown in Listing 42) is in the directory named WTK001.

As you can see from the code in Listing 42, the MIDlet program contains the following package directive:

package WTK001;

I will leave it up to you to discern the meaning of the package directive in this case. Just make certain that you duplicate the directory structure and the package directive. If you change either, you must change both.

Batch file format

The second housekeeping chore that I will explain is the format of the batch file. Since a batch file is little more than a text file containing commands that you might normally execute at the command-line prompt, each such command must be on a line of its own in the batch file. (If there is such a thing as a line continuation indicator in a batch file, I don't know what it is.)

However, the commands in this case are much too long to fit into this narrow publication format. Therefore, I manually inserted line breaks in Listing 5 (and all of the remaining batch files as well) to force the material to fit in the required publication format. You will have to remove those line breaks when you copy the code into your own batch files.

Pointing to WTK2.5.2 software

The default installation location for the wireless toolkit software is C:\WTK2.5.2 on a Windows machine. However, you can install it wherever you like.  For example, I have it installed on the M-drive on my machine rather than the C-drive. Therefore, the batch file begins by setting an environment variable named WTK2.5.2 to indicate the location of the toolkit. This environment variable can be used later, as in

%WTK2.5.2%
to represent the path to the toolkit in the batch commands.

The javac command

Listing 5 assumes that you have J2SE v1.4 or later installed on your machine, and that the program file named javac.exe is "on the path".

According to the Sun documentation, the first two options following the javac command in Listing 5 instruct the compiler to do the following (where <release> specifies a particular Java release version):

  • -target <release> Generate class files for specific VM version
  • -source <release> Provide source compatibility with specified release

As you can see, Listing 5 targets the output from the compiler for compatibility with version 1.4 of the Java Virtual Machine. (I explained the reasons for this earlier.)

The bootclasspath option

The next option to javac (-bootclasspath in Listing 5) instructs the compiler to "Override the location of bootstrap class files." The two elements in the classpath that follow specify the paths to the WTK2.5.2 JAR files for CLDC 1.1 and MIDP 2.0 respectively. Thus, the classes in those two JAR files will be used to compile the program instead of the classes in the standard J2SE class library.

If you want to compile to a different version of CLDC and/or MIDP, look in the lib directory of WTK2.5.2 and get the names of the JAR files for the configuration and profile that you want to use. Substitute those names for the names given in Listing 5.

The MIDlet program to be compiled

Finally, the last command-line parameter to the javac program in Listing 5 instructs the compiler to compile the program named WTK001.java in the directory named WTK001.

If there are compiler errors, they will appear on the command-line screen at this point when you run the batch file.

A directory listing of class files

Before pausing, the batch file in Listing 5 produces a directory listing of all the class files in the WTK001 folder for later use.

The pause command

The pause command at the end of Listing 5 causes the command-line screen to remain visible until you press any key on the keyboard. This is useful because it allows you to execute the batch file simply by double-clicking it with the mouse in the Windows GUI. Because of the pause command, the command-line screen won't go away until you dismiss it.

An exercise for the student
A useful upgrade would be for you to figure out how to cause my framework to capture and display the error messages produced by the compiler. I will show you how to do this in a future lesson, but it would be a good exercise for you at this point.

Keep a copy of this batch file

Once you begin using my framework program to develop and test your MIDlet programs, this batch file will be very handy to keep around for diagnostic purposes. If there is a compiler error when you run my framework program, you will simply get a single-line error message indicating a compiler error. At that point, you can use the batch file in Listing 5 to recompile the program and see the error messages produced by the javac program.

A sample text file

The next operational step will be for you to run the batch file named 3-Preverify.bat in order to preverify each of the class files produced during the compilation of the MIDlet program. Assuming that there are multiple class files, you have two options as to how you go about doing this:

  1. Run the preverify.exe program multiple times, or
  2. Take advantage of one of the options to the preverify.exe program and run the program once against a list of class files to be preverified.

A text file containing a list of class files

Listing 6 shows a sample text file for the MIDlet program named WTK001 that can be used with the second option.

Listing 6. A sample text file named 2-Classes.txt.

WTK001.WTK001$1LocalClass 
WTK001.WTK001$MemberClass 
WTK001.WTK001

Once again, it was necessary for me to manually insert line breaks into Listing 6 to force the material to fit in the required narrow format. However, it is very important that all of the items in the list be on a single line in the text file and that the items be separated by a space character. Each item in the list is the path from the execution directory of the batch file to the class file itself. Note also that there is no extension on the name of the class files.

As you may recall, the batch file in Listing 5 produces a directory listing of all the class files. I did this to help you to construct the list of files shown in Listing 6 with minimal effort.

The preverify method

Figure 3 shows usage instructions for the preverify method. Listing 7 shows the contents of the batch file used to execute the method and preverify each of the class files produced by the compilation process in Listing 5. The options that were used in this batch file are highlighted in boldface in Figure 3.

Figure 3. Usage instructions for the preverify method.

Usage: preverify [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 
                  (default is ./output/)
   [ -cldc1.0 | -cldc ]
                  Checks for existence of language 
                  features prohibited by CLDC 1.0 
                  (native methods, floating point and 
                  finalizers)
   -target <CLDC1.1 | CLDC1.0>
                  Which preverifier to run
   -nofinalize    No finalizers allowed
   -nonative      No native methods allowed
   -nofp          No floating point operations allowed
   @<filename>    Read command line arguments from a text
                  file
                  Command line arguments must all be on a 
                  single line
                  Directory names must be enclosed in 
                  double quotes (")

The batch file named 3-Preverify.bat

As you may already have figured out, the numbers that I included in the batch-file names indicate the order in which they should be executed. The batch file used to preverify the class files in the list shown in Listing 6 is shown in Listing 7.

Listing 7. The batch file named 3-Preverify.bat.

rem Set path to root of wireless toolkit directory tree.
set WTK2.5.2=M:\WTK2.5.2

%WTK2.5.2%\bin\preverify.exe 
-classpath %WTK2.5.2%\lib\cldcapi11.jar;
%WTK2.5.2%\lib\midpapi20.jar 
@2-Classes.txt

pause

As was the case earlier, Listing 7 uses a -classpath option to specify CLDC version 1.1 and MIDP version 2.0.

Listing 7 also specifies that the text file named 2-Classes.txt contains the list of class files that are to be preverified.

Location of the preverified class files

Figure 3 indicates that unless overridden by use of the -d parameter, the output class files from the preverify program will be written into a directory named ./output/. This directory will be a subdirectory of the execution directory of the batch file, making it a sibling of the directory named WTK001. Also, the preverify program honors the package directive in the MIDlet program. This causes the preverified class files to be written into the following directory:

output/WTK001

Creating a manifest file

One of the things that we will be doing shortly is making a JAR file containing the preverified class files plus a manifest file.

There is nothing difficult about creating the manifest file. The only tricky part is knowing what the manifest file must contain. By examining several resources on the web, I concluded that as a minimum the manifest file must contain the three lines of text shown in Figure 4. (See the article by Richard Marejka in Resources for example.)

Figure 4. Required contents for the manifest file.

MIDlet-Name: WTK001
MIDlet-Version: 1.0.0
MIDlet-Vendor: Dick Baldwin

However, I was completely unable to determine the significance of the MIDlet version number shown in Figure 4. The Sun cell phone emulator doesn't seem to care what value is provided for this item in the manifest file (and later in the JAD file).

Batch file named 4-MakeManifestFile.bat

Listing 8 shows a batch file that can be used to create the necessary manifest file.

Listing 8. Batch file named 4-MakeManifestFile.bat.

cd output
del Manifest.mf

echo MIDlet-Name: WTK001>> Manifest.mf
echo MIDlet-Version: 1.0.0>> Manifest.mf
echo MIDlet-Vendor: Dick Baldwin>> Manifest.mf

type Manifest.mf
pause

If you are familiar with command-line control of your system, you shouldn't find anything in Listing 8 that you don't understand. If you do, just look it up on the web and improve your knowledge in the process.

The batch file named 5-MakeJarFile.bat

The program used to create a JAR file is part of J2SE. The name of the program is jar.exe, and it is normally found in the bin directory. Listing 9 shows a batch file that can be used to create the JAR file.

Listing 9. The batch file named 5-MakeJarFile.bat.

echo off
cd output
jar cvfm WTK001.jar Manifest.mf .\WTK001
echo ===
echo Note size of jar file.
dir *.jar
pause

Once again, there is nothing unusual about the code in Listing 9. If you don't know what it means, go to a command line and enter the jar command. You should get usage information on your screen that will explain the use of the jar command in Listing 9.

In order to create a JAD file later, it is necessary to know the exact size of the JAR file in bytes. The code in Listing 9 causes the file size to be displayed on the screen to make it easily available for later use.

Make a JAD file

Ultimately, all that is needed to deploy a MIDlet into the Sun cell phone emulator, or into an actual cell phone is the JAR file and a JAD file. As with the manifest file, the most difficult part of creating the JAD file is determining what the JAD file must contain. Once again, by consulting several resources on the web, I concluded that the minimum requirements for the JAD file are shown in Figure 5.

Figure 5. Minimum requirements for a JAD file.

MIDlet-1: WTK001, , WTK001.WTK001
MIDlet-Name: WTK001
MIDlet-Version: 1.0.0
MIDlet-Vendor: Dick Baldwin
MIDlet-Jar-URL: WTK001.jar
MIDlet-Jar-Size: 2123
MicroEdition-Profile: MIDP-2.0
MicroEdition-Configuration: CLDC-1.1

Partial duplicate of the manifest file

As I understand it, the three boldface lines in Figure 5 must be exact duplicates of the same three lines in the manifest file (see Figure 4). The value shown for the size of the JAR file must be the exact size of the file in bytes. I don't know what would happen if you failed to make the MIDP version and the CLDC version match the versions that you specified when you compiled the MIDlet code, but I would think that they should normally match.

That leaves only the first line in Figure 5 as not being self-explanatory. This line has three parameters separated by commas. According to one source on the web, the three parameters for that line describe the following:

<Application name>, <icon path>, <MIDlet class>

The icon parameter was left blank in Figure 5.  The last parameter instructs the virtual machine to execute the class at WTK001.WTK001.

The batch file named 6-MakeJadFile.bat

Listing 10 contains a batch file capable of producing the JAD file shown in Figure 5.

Listing 10. The batch file named 6-MakeJadFile.bat.

echo off
echo USAGE: 6-MakeJadFile jarFileSizeInBytes

cd output
del WTK001.jad

echo MIDlet-1: WTK001, , WTK001.WTK001>> WTK001.jad
echo MIDlet-Name: WTK001>> WTK001.jad
echo MIDlet-Version: 1.0.0>> WTK001.jad
echo MIDlet-Vendor: Dick Baldwin>> WTK001.jad
echo MIDlet-Jar-URL: WTK001.jar>> WTK001.jad
echo MIDlet-Jar-Size: %1>> WTK001.jad
echo MicroEdition-Profile: MIDP-2.0>> WTK001.jad
echo MicroEdition-Configuration: CLDC-1.1>> WTK001.jad

type WTK001.jad

pause

The code in this batch file is completely straightforward with one exception. Note the use of the %1 in the line that specifies the size of the JAR file. This makes it possible to run this batch file from the command line by entering the JAR file size as a command-line parameter.

Run the cell phone emulator

That leaves us with one more batch file to examine. If you have made it this far, execution of the batch file shown in Listing 11 should run the cell phone emulator producing an output similar to that shown in Figure 2.

Listing 11. The batch file named 7-Run.bat.

rem Set path to root of wireless toolkit directory tree.
set WTK2.5.2=M:\WTK2.5.2

cd output

%WTK2.5.2%\bin\emulator.exe -Xdescriptor WTK001.jad

pause

Once the MIDlet is loaded into the emulator, the word launch should appear in the bottom-right corner of the cell phone screen. Use the mouse to click the button immediately below that word. That should cause the MIDlet to be executed once, causing my name to appear on the cell-phone screen for about ten seconds. Then it will go away.

The -Xdescriptor parameter value of WTK001.jad in Listing 11 causes the virtual machine to open the JAD file to get instructions on how to proceed further.

Problems with the batch file approach

Entering the commands manually at the command-line prompt is very tedious and prone to typing errors. Encapsulating those commands in batch files and executing the batch files in the proper sequence improves things considerably. However, as near as I have been able to determine, it isn't possible to totally automate the process using batch files.

There are two places where manual intervention is required when using the batch file approach. At one point, it is necessary to manually create the text file containing the list of class files to be preverified. At another point, it is necessary to manually enter the size of the JAR file as a command-line parameter. Ideally, we would like to eliminate the requirement to manually intervene in the process. That provides a good transition into the discussion of my fully automated framework program written in Java v1.6.

The framework program named WTKFramework01

The purpose of this program is to provide a framework that makes it easy to experiment with Java MIDlets written to run on small mobile devices using the Sun Java Wireless Toolkit (WTK2.5.2).

The framework not only makes such experimentation easy, it also cleans up after itself by automatically deleting all of the extraneous files created during the development of the JAR and JAD files. These are the only files that are required for the deployment of the MIDlet program.

Overall behavior of the program

Given a file containing the source code for the MIDlet, a single click of the mouse causes this framework to automatically cycle through the following steps:

  • Compile (targeted to Java v1.4 virtual machine)
  • Preverify
  • Create manifest file
  • Create JAR file
  • Create JAD file
  • Delete extraneous files, saving the source, JAR and JAD files
  • Deploy and execute in Sun's cell phone emulator

Testing
The framework program was tested using Java SE 6 and WTK2.5.2 running under Windows XP.

Disk organization

The MIDlet being processed must be stored in a folder having the same name as the main MIDlet class. The folder containing the MIDlet must be a child of the folder in which the framework is being executed.

Will discuss in fragments

A complete listing of the framework program named WTKFramework01 is presented in Listing 43. As usual, I will discuss the program in fragments.

The beginning of the class, including import directives and the declaration of a large number of instance variables is shown in Listing 12.

Listing 12. Beginning of class named WTKFramework01.

import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class WTKFramework01{
  String toolkit = "M:/WTK2.5.2";//Path to toolkit root
  String prog = "WTK001";//Default program name
  String vendor = "Dick Baldwin";//Default vendor name
  String midletVersion = "1.0.0";
  //Path to the bin folder of the Java installation
  String javaPath = "C:/Program Files/Java/jdk1.6.0/bin";
  
//Many variable declarations deleted from this listing for
// brevity.  See them all in Listing 43

  JRadioButton cButton10;
  JRadioButton cButton11;

The main method

The main method simply instantiates a new object of the member class named GUI, which I will explain next.

Listing 13. The main method.

  public static void main(String[] args){
    new WTKFramework01().new GUI();
  }//end main

The user interface

The class named GUI creates and displays the user interface shown in Figure 1. This interface allows the user to enter the name of the MIDlet program to be processed along with some other pertinent text information that will be used while the program is running.

For convenience, a default value is provided for each of the text fields. Default values were chosen that are appropriate for my system. You may want to modify the default values to make them more appropriate for your system. You can do this by modifying the initial values of the five instance variables shown in boldface in Listing 12.

The user interface also provides radio buttons that allow you to select a profile (MIDP) and a configuration (CLDC) for use by the program. You can also change the default selection should you choose to do so. I will explain how to change the default selection in conjunction with the code that produces the radio buttons.

When the user clicks the Run button shown in Figure 1, the framework program executes, hopefully causing the MIDlet to run in the cell phone emulator shown in Figure 2. Once the window containing the emulator is dismissed, the framework program is ready to accept a new set of input parameters (such as a new program name or a different profile) and run again.

At the end of each run, the JAR file and the JAD file for that MIDlet program and those parameters are available in the output folder to be used for deployment in an actual cell phone.

Beginning of the inner class named GUI

The GUI class begins in Listing 14.

Listing 14. Beginning of the inner class named GUI.

  class GUI extends JFrame{

    GUI(){//constructor
      setSize(400,250);
      setTitle("Copyright 2007, R.G.Baldwin");

//Code deleted for brevity in this listing.
//See Listing 43 for complete code listing.

      getContentPane().add(proCon,"Center");

Note that essentially all of the code in the GUI class is contained in the constructor.

The code in Listing 14 is completely straightforward. In fact, it is so straightforward that I deleted much of the code from Listing 14 for brevity. You can view that code in its entirety in Listing 43.

Specifying default radio buttons

I promised you earlier that I would explain how to establish the default selection for the radio buttons in conjunction with the explanation of the code. That process is illustrated in Listing 15. The five radio buttons are instantiated using two different overloaded versions of the JRadioButton constructor in Listing 15. When a radio button is constructed using the constructor that takes two parameters, and true is specified for the second parameter, that button is selected by default when it is constructed.

Listing 15. Specifying default radio buttons.

      //Construct JPanel with radio buttons for selection
      // of profile.
      JPanel proButtons = new JPanel();
      proButtons.setLayout(new GridLayout(0,1));
      pButton10 = new JRadioButton("MIDP-1.0");
      pButton20 = new JRadioButton("MIDP-2.0",true);
      pButton21 = new JRadioButton("MIDP-2.1");

//Code deleted for brevity in this listing.
//See Listing 43 for complete code listing.

      //Construct JPanel with radio buttons for selection
      // of configuration.
      JPanel configButtons = new JPanel();
      configButtons.setLayout(new GridLayout(0,1));
      cButton10 = new JRadioButton("CLDC-1.0");
      cButton11 = new JRadioButton("CLDC-1.1",true);

//Code deleted for brevity in this listing.
//See Listing 43 for complete code listing.

      proCon.add(configButtons);

As before, most of the code in Listing 15 is completely straightforward. Therefore, much of the code was deleted from Listing 15 for brevity. You can view that code in its entirety in Listing 43.

Register an ActionListener on the Run button

As mentioned earlier, when the user clicks the Run button in Figure 1, the framework program is executed using the parameter values provided by the user in the user interface.

Listing 16 shows an anonymous inner class that registers an ActionListener on the Run button.

Listing 16. An anonymous ActionListener class.

      //Register an action listener on the Run button
      runButton.addActionListener(
        new ActionListener(){
          public void actionPerformed(ActionEvent e){
            //Get user inputs from text fields

//Code deleted for brevity in this listing.
//See Listing 43 for complete code listing.

            //Now run the program.
            runTheProgram();

          }//end actionPerformed
        }//end new ActionListener
      );//end addActionListener

There is nothing unusual about the code in the definition of the anonymous ActionListener class, so I deleted most of the code from Listing 16 for brevity. As before, you can view that code in its entirety in Listing 43.

Purposes of the ActionListener object

The ActionListener object has two primary purposes:

  1. Extract information from the user input GUI in Figure 1 to set parameter values that will be used by the framework to process the specified MIDlet.
  2. Cause the framework program to execute and process the MIDlet program one time.

Once the first purpose has been accomplished, the code in Listing 16 calls the method named runTheProgram to cause the framework program to execute one time. Thus, each time the Run button is clicked, the framework program executes and processes the specified MIDlet program one time.

Make the GUI visible

Listing 17 shows the end of the definition of the class named GUI.

Listing 17. Make the GUI visible.

      setVisible(true);
    }//end constructor
  }//end class GUI

The code in Listing causes the GUI shown in Figure 1 to become visible on the screen. Listing 17 also signals the end of the constructor and the end of the GUI class.

The method named runTheProgram

As indicated above, when the user clicks the Run button in Figure 1, the framework program is caused to execute one time using the input parameters specified by the user in the GUI. This is a relatively long method. The beginning of the method is shown in Listing 18.

Listing 18. Beginning of method named runTheProgram.

  void runTheProgram(){
    System.out.println("PROGRESS REPORT");
    System.out.println("Running program named: " + prog);

This method calls several other methods in sequence to accomplish the needed actions. If there is a failure at any step along the way, the framework will terminate at that point with a suitable error message.

Sample screen output

A considerable amount of information is displayed on the command-line screen while the framework program is executing to keep you informed as to what is happening. For example, the code in Listing 18 produced the screen output shown in Figure 6 for one particular run.

Figure 6. Screen output at the beginning of a run.

PROGRESS REPORT
Running program named: WTK001

Although this isn't the case in Figure 6, much of the screen output produced by the framework program is too wide to fit in this narrow publication format. Therefore, it will frequently be necessary for me to manually enter line breaks in order to show you samples of the screen output in this lesson.

Delete leftover files from a previous run

The first method called by the method named runTheProgram is a method named deleteOldStuff as shown in Listing 19. The purpose of this method call is to delete leftover files from a previous run, if any exist.

Listing 19. Delete leftover files from a previous run.

    deleteOldStuff();
    if(initialCleanupOK != 0){//Test for success
      System.out.println("Initial cleanup error");
      System.out.println("Terminating");
      System.exit(1);
    }//end if

The instance variables include a number of "success flags" of which the variable named initialCleanupOK is one. These variables are initialized to a value of 1. A successful execution of the method will change that value to a 0. The code in Listing 19 is typical, causing the value of the success flag to be tested, and causing the program to terminate with an appropriate error message if that value is not 0.

The method named deleteOldStuff

Now getting back to the code in Listing 19, I am going to put the discussion of the method named runTheProgram on hold for a while and explain the method named deleteOldStuff.

Listing 20. Beginning of the method named deleteOldStuff.

  void deleteOldStuff(){
    System.out.println(
           "Deleting leftover files from a previous run");

    //Delete subdirectory from output folder if it exists.
    int successFlag = deleteOutputSubDir();

As the name implies, the purpose of this method is to clean up the disk area utilized by the framework program deleting old files, if any, that may have been left there from a previous run of the program.

The directory named output

As you will learn later, the preverify method in WTK2.5.2 causes its output to be written in a directory named output, which is a subdirectory of the directory in which the program is being run. The preverify method honors the package directive in the MIDlet program. Therefore, for my case, using the package directive shown in Listing 1, the preverified files will be written in a subdirectory of the output directory where the name of that subdirectory matches the name of the MIDlet program.

Upgrading deleteOutputSubDir
If you elect to cause your package directives to have more depth than mine, you may need to upgrade the method named deleteOutputSubDir to a (possibly recursive) method that will delete all of the children of the subdirectory. I could do that myself, but I have decided to "leave that as an exercise for the student".

The code in Listing 20 calls the method named deleteOutputSubDir to delete that subdirectory and the files that it contains.

Put deleteOldStuff on hold

At this point, I will put the discussion of the method named deleteOldStuff on hold and explain the method named deleteOutputSubDir.

Subdirectory should not exist
Actually this subdirectory should not exist. It should have been deleted during the final cleanup process in the previous run. However, if it does exist, it will be deleted.

The beginning of the method named deleteOutputSubDir

This method begins in Listing 21.  As explained earlier, the purpose of this method is to delete the subdirectory contained in the output directory and all of its files (see sidebar).

Listing 21. The beginning of the method named deleteOutputSubDir.

  int deleteOutputSubDir(){
    int returnVal = 0;

    System.out.println(
                  "\nDeleting files from output folder.");
    //First delete the files in the subdirectory
    File subDir = new File("output/" + prog);
    //Get a list of the files in the folder.
    String[] children = subDir.list();
    if(children != null){
      for(int cnt = 0;cnt < children.length;cnt++){
        boolean success = (new File("output/" + prog + "/"
                               + children[cnt])).delete();
        if(!success){
          // Deletion failed
          returnVal = 1;
        }else{
          System.out.println(
                      "   " + children[cnt] + " deleted");
        }//end else
      }//end for loop
    }//end if !null

Assumes no additional depth

Listing 21 begins by using a straightforward approach to delete the files contained in the subdirectory under the assumption that there are no subdirectories in that subdirectory. If the subdirectory does contain other subdirectories (resulting from additional depth in the package directive), the deleteOutputSubDir method will likely throw an exception (see sidebar). Note in particular the handling of the success flag in this code.

Delete the subdirectory proper

Having deleted all of the files, the code in Listing 22 deletes the subdirectory proper.

Listing 22. Delete the subdirectory proper.

    if(subDir.exists()){
      boolean success = subDir.delete();
      if(!success){
        // Deletion failed
        returnVal = 1;
      }else{
        System.out.println("   Empty directory named " 
                         + "output/" + prog + " deleted");
      }//end else
    }//end if

    return returnVal;
  }//end deleteOutputSubDir

Note that the value of returnVal, which is based on the success of the deletion process, is returned and stored in the variable named successFlag in Listing 20.

Delete old manifest, JAR, and JAD files

Returning now to the discussion of the method named deleteOldStuff, Listing 23 shows the code that will delete the old manifest, JAR, and JAD files if they exist.

Listing 23. Delete old manifest, JAR, and JAD files.

    //Delete manifest file if it exists.
    File manifestFile = new File("output/Manifest.mf");
    if(manifestFile.exists()){
      boolean success = manifestFile.delete();
      if(success){
        System.out.println("   Manifest file deleted");
      }else{
        successFlag = 1;
      }//end else
    }//end if
    
    //Delete old JAR file if it exists.
    File jarFile = new File("output/" + prog + ".jar");
    if(jarFile.exists()){
      boolean success = jarFile.delete();
      if(success){
        System.out.println("   Old jar file deleted");
      }else{
        successFlag = 1;
      }//end else
    }//end if

    //Delete old JAD file if it exists.
    File jadFile = new File("output/" + prog + ".jad");
    if(jadFile.exists()){
      boolean success = jadFile.delete();
      if(success){
        System.out.println("   Old jad file deleted");
      }else{
        successFlag = 1;
      }//end else
    }//end if

The JAR file and the JAD file should exist, because they are not deleted during the final disk cleanup at the end of the run. However, the manifest file will normally have been deleted during that cleanup process.

The code in Listing 23 is completely straightforward and shouldn't require further explanation.

Delete old class files from the program directory

Listing 24 calls a method named deleteProgClassFiles, (which I will discuss shortly) to delete any leftover class files in the program directory if they exist. (There shouldn't be any. They should have been deleted in the final cleanup process in the previous run.)

Listing 24. Delete old class files from the program directory.

    int temp = deleteProgClassFiles();
    if(temp != 0){
      successFlag = temp;
    }//end if

    if(successFlag == 0){
      System.out.println("\nLeftover files deleted");
      initialCleanupOK = 0;//success
    }else{
    }//end else

  }//end deleteOldStuff

The code in Listing 24 also deals with the success flag named initialCleanupOK, setting it to 0 if no errors occurred during the process of deleting the old files and folders.

Listing 24 also signals the end of the method named deleteOldStuff.

The method named deleteProgClassFiles

The method named deleteProgClassFiles is shown in its entirety in Listing 25.

Listing 25. The method named deleteProgClassFiles.

  int deleteProgClassFiles(){
    int returnVal = 0;

    System.out.println(
        "\nDeleting class files from program folder.");
    File dir = new File(prog);
    //Get a list of the files in the folder.
    String[] children = dir.list();
    for(int cnt = 0;cnt < children.length;cnt++){

      //Don't delete files with extension of .java
      String aFile = children[cnt];
      if(aFile.indexOf(".java") < 0){
        //Not a .java file.
        boolean success = (
                   new File(prog + "/" + aFile).delete());
        if(!success){
          // Deletion failed
          returnVal = 1;
        }else{
          System.out.println("   " + aFile + " deleted");
        }//end else
      }else{
        //This is a .java file.
        System.out.println("   " + aFile + " saved");
      }//end else

    }//end for loop

    return returnVal;
  }//end deleteProgClassFiles

Although the code in Listing 25 is straightforward, one interesting aspect of that code is that it does not delete the source code files having an extension of .java in the program directory. (After all, we don't want to delete the program files before we attempt to compile them.)

Typical screen output from deleteOldStuff method

Figure 6 shows typical screen output produced by the call to the method named deleteOldStuff in Listing 19.

Figure 6. Typical screen output from deleteOldStuff method.

Deleting leftover files from a previous run

Deleting files from output folder.
   Old jar file deleted
   Old jad file deleted

Deleting class files from program folder.
   WTK001.java saved

Leftover files deleted

In this case, the only leftover files from the previous run were the JAR and JAD files, which are always preserved at the end of a run.

Compile the MIDlet

After twenty-listings and six figures, we are finally going to compile the MIDlet. (I didn't promise you that this program would be short and simple, just effective.)

Listing 26 shows a fragment from the method named runTheProgram, which calls the method named compile to compile the MIDlet.

Listing 26. Compile the MIDlet.

    compile();//compile the MIDlet
    if(compileOK != 0){//Test for successful compilation.
      System.out.println("Terminating");
      System.exit(1);
    }//end if

Beginning of the method named compile

The beginning of the method named compile is shown in Listing 27.

Listing 27. Beginning of the method named compile.

  void compile(){
    try{
      String cmdString =
           javaPath + "/javac -target 1.4 -source 1.4 "
           + "-bootclasspath " + toolkit + configJar + ";"
           + toolkit + profileJar
           + " " + prog + "/" + prog + ".java";
      
      System.out.println("\nCompile command:");
      System.out.println(cmdString);
      Process proc = Runtime.getRuntime().exec(cmdString);

The purpose of this method is to compile the MIDlet. As was the case for the batch file shown in Listing 5, the compilation generates class files for a Java v1.4 virtual machine. In fact, the code in Listing 27 does essentially the same thing as the batch file in Listing 5, but does so in a different way.

New processes
When you transfer control to a new process window by calling the exec method, as this program does, the path environment variable doesn't go along for the ride. Therefore, you must provide the full path for programs that you call in that new process.

Calling the exec method

The code in Listing 27 is not straightforward, and there is a good possibility that you have never used code like this before. In particular, the code in Listing 27 constructs a command string and then calls the method named exec to cause that command to be executed on the command line in a new process.

The javac command string

Although it may not be immediately obvious from looking at the code, the javac command string that is constructed Listing 28 is in exactly the same format as the javac command in the batch file in Listing 5. The main difference is in the use of variables that contain user input values to construct the string. Since I explained that command in the discussion of that batch file, I won't repeat that discussion here.

Typical output from the compile process

Before leaving the topic of the command string, I want to show you some typical output from this part of the program. That output is shown in Figure 7. (Once again, I manually inserted line breaks to force the material to fit into this narrow publication format.)

Figure 7. Typical output from the compile process.

Compile command:
C:/Program Files/Java/jdk1.6.0/bin/javac 
-target 1.4 
-source 1.4 
-bootclasspath 
M:/WTK2.5.2/lib/cldcapi11.jar;
M:/WTK2.5.2/lib/midpapi20.jar 
WTK001/WTK001.java
Compile OK

Display of the command string

The boldface portion of the text in Figure 7 shows the command string that is passed to the new process created by the execution of the exec method. You should be able to reconcile the differences between this command string and the command that is shown in the batch file in Listing 5, but just in case you can't, I will give you some help.

The first difference is the use of a full path to the javac program in Figure 7 (See the sidebar discussing the path environment variable and new processes.).

The second difference is that the command in Figure 7 is exact whereas the command in the batch file in Listing 5 uses %WTK2.5.2% to represent the root of the WTK2.5.2 directory tree.

Compiler error messages are not displayed
This version of the framework does not display detailed compiler error messages. It simply indicates that the compilation has failed and terminates. If a compilation fails, the next step is to use a batch file similar to that shown in Listing 5 to perform the compilation on a stand-alone basis. (I will improve on that process in a future lesson.) That will cause compilation error messages to be displayed and allow for identifying and fixing the problem in the MIDlet code.

Delay until compilation is complete

When you call the exec method to execute a command at the command line in a new process, you must be cognizant of the fact that the exec method may return before the new process is finished.

Continuing with the method named compile, the code in Listing 28 delays until the compilation is complete. In other words, the method named waitFor blocks and does not return until the compiler terminates. The exit value from the compiler is returned and stored in the variable named val.

Listing 28. Delay until compilation is complete.

      int val = proc.waitFor();
      compileOK = val;
      if(val == 0){
        System.out.println("Compile OK");
      }else{
        System.out.println("Compile error");
      }//end else

    }catch( Exception e ){
      e.printStackTrace();
    }//end catch

  }//end compile

Test the exit value for a successful compilation

Then the code in Listing 28 tests the exit value for success. By convention, an exit value of 0 indicates that the compilation was completed successfully. Any other value indicates that the compilation was not successful. If the exit value is 0, it is stored in the success flag variable named compileOK and the method terminates. Otherwise, the method displays a Compile error message and terminates without changing the value stored in the success flag named compileOK.

As mentioned earlier, the value of the compileOK flag was initialized to 1. Referring back to Listing 26, we see that if the method named compile fails to set the value of that flag to 0, the framework program will terminate. This general procedure is used to prevent the framework from continuing to run after a failure in one of the steps implemented in the method named runTheProgram.

Preverify the class files

Returning once again to the method named runTheProgram, the code in Listing 29 calls the preverify method to preverify the class files produced by the compiler. This framework terminates with an appropriate error message if the preverify method fails.

Listing 29. Preverify the class files.

    preverify();//Pre-verify the MIDlet class files
    if(preverifyOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

Beginning of the preverify method

The preverify method begins in Listing 30.

Listing 30. Beginning of the preverify method.

  void preverify(){
    System.out.println(
        "\nPreverifying class files.");
    File dir = new File(prog);
    //Get a list of the files in the folder.
    String[] children = dir.list();
    for(int cnt = 0;cnt < children.length;cnt++){
      //Only preverify files with an extension of .class
      String aFile = children[cnt];
      if(aFile.endsWith(".class")){
        //This is a class file
        try{
          //Remove the .class extension from the file name
          aFile = aFile.substring(0,aFile.lastIndexOf(
                                               ".class"));

The compiler may have created any number of class files. The preverify method assumes that they are all contained in the same folder, and assumes that the folder may contain other types of files such as source code files.

The code in Listing 30 shows the beginning of a for loop that gets the name of each class file contained in the folder and removes the .class extension from that file name.

Continuation of the preverify for loop

The for loop that began in Listing 30 continues in Listing 31.

Listing 31. Continuation of the preverify for loop.

          String cmdString = 
                toolkit + "/bin/preverify.exe -classpath "
                + toolkit + configJar + ";"
                + toolkit + profileJar + " "
                + prog + "/" + aFile;

          System.out.println("\nPreverify command");
          System.out.println(cmdString);
          Process proc = Runtime.getRuntime().exec(
                                               cmdString);

The code in Listing 31 constructs a command string and passes that command string as a parameter to the exec method for execution.

Name duplication
In hindsight, it would probably have been better for me to have given the method a different name so as to avoid duplicating the name of the WTK program named preverify.

A different approach than with the batch file

This Java program takes a different approach to preverification than was the case for the batch file shown in Listing 7. As you will recall, using the batch file approach, it was necessary for the user to manually create a text file containing the names of all the class files to be preverified. The name of the text file was then provided as a command-line parameter to the preverify program provided by WTK2.5.2.

In that case, the preverify program was executed only once by the batch file and it processed all of the files listed in the text file.

Programmatic determination of class file names

In this program, Java code is used to determine the names of all the class files with no manual intervention required on the part of the user. Each class file is then processed during one iteration of a for loop.

The command string for one class file is displayed during each iteration of the for loop. One such command string is shown in the screen output in Figure 8. This command string instructs the WTK preverify program to process the class file named WTK001/WTK001$1LocalClass. This is the class file that represents the local class in the MIDlet program shown in Listing 1.

Figure 8. Typical screen output for preverification process.

Preverify command
M:/WTK2.5.2/bin/preverify.exe 
-classpath M:/WTK2.5.2/lib/cldcapi11.jar;
M:/WTK2.5.2/lib/midpapi20.jar 
WTK001/WTK001$1LocalClass

...

Pre-verify OK

Delay until preverification is complete

As before, after calling the exec method, it is necessary to delay until preverification is complete before executing the next iteration of the for loop. This is accomplished by the code in Listing 32.

Listing 32. Delay until preverification is complete.

          int val = proc.waitFor();
          preverifyOK = val;
          if(val == 0){
            System.out.println("Pre-verify OK");
          }else{
            System.out.println("Pre-verify error");
            return;//return prematurely
          }//end else

        }catch( Exception e ){
          e.printStackTrace();
        }//end catch
      }//end if
    }//end for loop
  }//end preverify

Listing 32 also signals the end of my method named preverify, and deals with the success flag named preverifyOK that is used by the runTheProgram code in Listing 29 to decide whether to continue or to terminate the framework program with an error message.

Delete the class files from the program directory

Getting back to the method named runTheProgram, Listing 33 shows another call to the method named deleteProgClassFiles in the method named runTheProgram for the purpose of deleting the class files from the program folder. This is a call to the same method shown earlier in Listing 25.

Listing 33. Delete the class files from the program directory.

    deleteClassFilesOK = deleteProgClassFiles();
    if(deleteClassFilesOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

I explained the method named deleteProgClassFiles earlier, so I won't repeat that explanation here. However, I will explain why I needed to delete those class files at this point in the program.

Why delete the class files?

Listing 9 shows the batch-file code that was used to create the JAR file using the batch file approach. Note that the second line of text in Listing 9 is a command that causes the current directory to be changed to the directory named output. From that point forward, the execution directory for the batch file is the directory named output. Under that circumstance, the jar command shown in Listing 9 produces a JAR file where each preverified class file in the JAR file has the correct path for matching the package directive in the source code in Listing 1.

How do you change the execution directory in program code?

However, if there is a way to cause the execution directory of the Java framework program to switch to the directory named output, I don't know what it is. Therefore, before creating the JAR file using program code, I need to move the preverified class files to the directory named WTK001 as a child of the program execution directory rather than leaving those files in the directory having the name WTK001 that is a child of the output directory.

Before moving the files, I need to delete the class files in the destination directory having the name WTK001. (Fortunately, I have no further use for those class files and it is okay to delete them at this point in the program execution.)

That is the explanation for why it was necessary to call the method named deleteProgClassFiles in Listing 33.

The screen output

Figure 9 shows the screen output produced by calling the method named deleteProgClassFiles in Listing 33.

Figure 9. Screen output from deleting class files.

Deleting class files from program folder.
   WTK001$1LocalClass.class deleted
   WTK001$MemberClass.class deleted
   WTK001.class deleted
   WTK001.java saved

Move the preverified files to the original program directory

The next statement in the method named runTheProgram is a call to the method named movePreverifiedFiles as shown in Listing 34.

Listing 34. Move the preverified files to the original program directory.

    movePreverifiedFiles();
    if(moveFilesOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

By now you should have caught onto the methodology for manipulating files and it shouldn't be necessary for me to explain the method named movePreverifiedFiles. You can view the method in its entirety in Listing 43.

Figure 10 shows the screen output produced by the call to the method named movePreverifiedFiles in Listing 34.

Figure 10. Screen output from moving files.

Moving preverified files to program folder.
   output/WTK001/WTK001$1LocalClass.class moved
   output/WTK001/WTK001$MemberClass.class moved
   output/WTK001/WTK001.class moved

Make the manifest file

Just as was the case when using the batch file approach, it is necessary for the framework program to create a manifest file named Manifest.mf in the directory named output. This is accomplished by calling the method named makeManifestFile as the next operation in the method named runTheProgram as shown in Listing 35.

Listing 35. Make the manifest file.

    makeManifestFile();
    if(manifestFileOK != 0){
      System.out.println("Manifest file error");
      System.out.println("Terminating");
      System.exit(1);
    }//end if

The method named makeManifestFile is completely straightforward and shouldn't require any explanation.  You can view that method in its entirety in Listing 43.

Make the JAR file

The method named runTheProgram calls the method named makeJarFile (as shown in Listing 36) to create the JAR file containing the manifest file and the preverified class files.

Listing 36. Make the JAR file.

    makeJarFile();
    if(jarFileOK != 0){
      System.out.println("JAR file error");
      System.out.println("Terminating");
      System.exit(1);
    }//end if

The method named makeJarFile

The method named makeJarFile is shown in its entirety in Listing 37.

Listing 37. The method named makeJarFile.

  void makeJarFile(){
    try{
      String cmdString = 
                    javaPath + "/jar cvfm output/" + prog 
                    + ".jar output/Manifest.mf ./" + prog;

      System.out.println("\nJAR command");
      System.out.println(cmdString);
      Process proc = Runtime.getRuntime().exec(cmdString);

      //Delay until complete
      int val = proc.waitFor();
      jarFileOK = val;
      if(val == 0){
        System.out.println("Jar file written");
      }else{
        System.out.println("Jar file error");
        return;//Return prematurely on error.
      }//end else

      //Get and save file size in bytes
      File file = new File("output/" + prog + ".jar");
      jarFileSize = file.length();
      System.out.println(
                     "   Jar file size = " + jarFileSize);

    }catch( Exception e ){
      e.printStackTrace();
    }//end catch

  }//end makeJarFile

Assuming that you understood the batch file code in Listing 9, and that you understand the process of creating command strings and causing them to be executed in a new process by calling the exec method, you should have no trouble understanding the code in Listing 37.

Get and save the JAR file size

The only thing that is new in Listing 37 is the code that gets and saves the size of the JAR file (highlighted in boldface). There is nothing complicated about this code. I mention it only because you haven't seen code like this before in this lesson.

The screen output from making the JAR file

Figure 11 shows the screen output produced by calling the method named makeJarFile in Listing 36.  Compare this with the contents of the batch file shown in Listing 9.

Figure 11. The screen output from making the JAR file.

JAR command
C:/Program Files/Java/jdk1.6.0/bin/jar
cvfm 
output/WTK001.jar 
output/Manifest.mf 
./WTK001
Jar file written
   Jar file size = 3066

Make the JAD file

The size of the JAR file shown in Figure 11 will be used to create the JAD file. This leads into the next action taken by the method named runTheProgram. (Recall that you had to deal with the file size issue manually when using the batch file approach. That issue is handled automatically in the framework program.)

Listing 38 calls the makeJadFile method to replicate the behavior of the batch file shown in Listing 10 to create a JAD file and to write it in the directory named output.

Listing 38. Make the JAD file.

    makeJadFile();
    if(jadFileOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

The method named makeJadFile shouldn't require further explanation. You can view that method in its entirety in Listing 43.

Clean up the disk

As mentioned earlier, I wanted my framework to avoid leaving any garbage files or directories that were created during the development process on the disk when the program terminates. The only two files that are required to deploy the MIDlet into the Sun cell phone emulator or into an actual cell phone are the JAR file and the JAD file. Therefore, the method named runTheProgram calls the method named cleanup in Listing 39 to clean up the disk.

Listing 39. Clean up the disk.

    cleanup();
    if(cleanupOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

The cleanup method is very similar to the method named deleteOldStuff that I explained earlier, so you should have no trouble understanding the cleanup method without an explanation on my part.

The screen output for the cleanup phase

Figure 12 shows the screen output produced by the call to the cleanup method in Listing 39.

Figure 12. The screen output for the cleanup phase.

Deleting files from output folder.
   Empty directory named output/WTK001 deleted
   Manifest file deleted

Deleting class files from program folder.
   WTK001$1LocalClass.class deleted
   WTK001$MemberClass.class deleted
   WTK001.class deleted
   WTK001.java saved

Extraneous files deleted

Note that the JAD file and the JAR file are not deleted from the directory named output. They remain there to be deployed into Sun's cell phone emulator for testing or into an actual cell phone when testing is complete.

Running Sun's cell phone emulator to test the MIDlet

That brings us to the end of the method named runTheProgram as shown in Listing 40.

Listing 40. Running the emulator.

    runEmulator();

    //Reset success flags
    initialCleanupOK = 1;//Success = 0
    compileOK = 1;//Compiler success = 0
    preverifyOK = 1;//Preverify success = 0
    deleteClassFilesOK = 1;//Delete success = 0
    moveFilesOK = 1;//Move success = 0
    manifestFileOK = 1;//Manifest success = 0
    jarFileOK = 1;//Jar file success = 0
    jadFileOK = 1;//Jad file success = 0
    cleanupOK = 1;//Cleanup success = 0

    //Control returns to here when the user terminates
    // the cell phone emulator.
    System.out.println(
      "\nClick the Run button to run another MIDlet.");
    System.out.println();//blank line

  }//end runTheProgram

The only thing that is really significant in Listing 40 is the call to the method named runEmulator.

The runEmulator method

The runEmulator method is shown in its entirety in Listing 41.

Listing 41. The runEmulator method.

  void runEmulator(){
    try{
      String cmdString = toolkit + "/bin/emulator.exe "
                + "-Xdescriptor output/" + prog + ".jad";

      System.out.println("\nEmulator command");
      System.out.println(cmdString);
      Process proc = Runtime.getRuntime().exec(cmdString);

      //Delay until complete
      int val = proc.waitFor();
      if(val == 0){
        System.out.println("Emulator finished");
      }else{
        System.out.println("Emulator error");
      }//end else

    }catch( Exception e ){
      e.printStackTrace();
    }//end catch

  }//end runEmulator

If everything goes well, two different screen outputs will be produced by the method named runEmulator. The most important output is the appearance of a cell phone emulator as shown in Figure 13, with the MIDlet loaded into the emulator.

The cell phone emulator output

For the MIDlet being discussed here, when the emulator first appears on the screen, there may be a short period during which the MIDlet is being loaded into the emulator and the cell phone screen will be blank except for the icons at the very top of the screen. Then the screen should change to look like Figure 13.

Figure 13. The cell phone emulator with the MIDlet loaded.

(There may be other text on the screen if you have loaded multiple MIDlets into the emulator.)

At this point, you should use the arrow keys on the cell phone keyboard to select WTK001 (if you have a choice). This should cause the word launch to appear in the gray area at the bottom right of the cell phone screen. (If you loaded only one MIDlet, it will already be selected and the word launch will already be visible once the load process is complete.)

The cell phone emulator with the MIDlet running

If you click the cell phone button below the word launch, the cell phone screen should change to that shown in Figure 14 with my name being displayed near the middle of the cell phone screen. The text at the top should also change to that shown in Figure 14.

Figure 14. The cell phone emulator with the MIDlet running.

After about ten seconds, the cell phone screen should go blank except for the icons at the top. When you dismiss the cell phone emulator window by clicking the X in the upper right corner, the text on the command-line screen should instruct you to "Click the Run button to run another MIDlet."

Screen output produced by the runEmulator method

The other screen output produced by the method named runEmulator is the information displayed on the command-line screen by print statements in the code. This output is shown in Figure 15.

Figure 15. Screen output produced by the runEmulator method.

Emulator command
M:/WTK2.5.2/bin/emulator.exe 
-Xdescriptor output/WTK001.jad
Emulator finished

Compare the command string shown in Figure 15 with the contents of the batch file shown in Listing 11, which I explained earlier.

That's a wrap!

That is the end of the discussion in the main body of this lesson.

Run the program

I encourage you to copy the code from Listing 42 and Listing 43 into your text editor, compile it, and execute it. Experiment with the MIDlet code in Listing 42 and the framework program in Listing 43, making changes, and observing the results of your changes. See if you can understand the results produced by your changes.

Don't forget that you will need to download and install the latest version of the Sun Java Wireless Toolkit for CLDC (see Resources). As of the date this lesson is being written (December 2007), the latest version of the toolkit is WTK2.5.2.

Summary

In this first lesson of the series, I presented some general background information about MIDlets. I also provided you with a Java programming framework that makes it easy to experiment with the programming of MIDlets. I also explained the Java code in the framework program.

What's next?

In the next lesson, I will teach you how to upgrade the framework program so that it will capture and display the standard output and error output produced by programs executing in a child process resulting from a call to the Runtime.getRuntime().exec(cmdString) method. For example, this will make it possible to display compile time errors when the framework is used to compile a MIDlet and will also make it possible to display information written to standard output or error output by MIDlets being tested in the Sun cell phone emulator.

Resources

Complete program listings

Complete listings of the programs discussed in this lesson are shown in Listing 42 and Listing 43 below.

Listing 42. The MIDlet program named WTK001.

/*WTK001.java
Copyright 2007, R.G.Baldwin

The purpose of this program is to illustrate a MIDlet
that is composed of linked objects from three classes,
one of which is a local class and one of which is a
member class.

Compilation produces the following three class files:
  WTK001$1LocalClass.class
  WTK001$MemberClass.class
  WTK001.class

Tested using a Java SE 6 compiler, targeted at a V1.4
virtual machine, and WTK 2.5.2 running under Windows XP.
*********************************************************/

package WTK001;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;

public class WTK001 extends MIDlet{

  Alert alertDisplay;

  public WTK001(){
    alertDisplay = new Alert("Name Display, WTK001");
    alertDisplay.setTimeout(10000);
    
    //==================================================//
    //This is a local class. The method named getName
    // instantiates a member class, gets the value of a
    // public instance variable from that object, and
    // returns that value.
    class LocalClass{
      public String getName(){
        return new MemberClass().name;
      }//end getName
    }//end class LocalClass
    //==================================================//

    //Get a name string from an object of a local class,
    // which in turn gets the actual name string from an
    // object of a member class.
    alertDisplay.setString(new LocalClass().getName());
  }//end constructor

  public void startApp(){
    Display.getDisplay(this).setCurrent(alertDisplay);
  }//end startApp

  public void pauseApp(){
  }//end pauseApp

  public void destroyApp(boolean unconditional){
  }//end destroyApp

  //====================================================//

  //This is a member class.
  class MemberClass{
    public String name = "Dick Baldwin";
  }//end class MemberClass
  //====================================================//
  
}//end class WTK001

 

Listing 43. The framework program named WTKFramework01.java.

/*WTKFramework01.java
Copyright 2007, R.G.Baldwin

The purpose of this program is to provide a framework that
makes it easy to experiment with Java MIDlets written to 
run on small mobile devices using the Sun Java Wireless 
Toolkit (WTK2.5.2).  The framework not only makes such 
experimentation easy, it also cleans up after itself by 
automatically deleting all of the extraneous files created
during the development of the JAR and JAD files, which
are required for the deployment of the MIDlet program.

Given a file containing the source code for the MIDlet, 
a single click of the mouse causes this framework to 
automatically cycle through the following steps:

Compilation (targeted to Java v1.4 virtual machine)
Pre-verification
Creation of the manifest file
Creation of the JAR file
Creation of the JAD file
Deletion of extraneous files, saving the JAR and JAD files
Deployment and execution in Sun's cell phone emulator

The MIDlet being processed must be stored in a folder 
having the same name as the main MIDlet class.  The 
folder containing the MIDlet must be a child of the 
folder in which the framework is being executed.

Note: When you transfer control to a new process window by
calling the exec method, the path environment variable
doesn't go along for the ride.  Therefore, you must 
provide the full path for programs that you call in that
new process.

Tested using Java SE 6 and WTK2.5.2 running under 
Windows XP.
*********************************************************/

import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class WTKFramework01{
  String toolkit = "M:/WTK2.5.2";//Path to toolkit root
  String prog = "WTK001";//Default program name
  String vendor = "Dick Baldwin";//Default vendor name
  String midletVersion = "1.0.0";
  String profile = "MIDP-2.0";
  String profileJar = "/lib/midpapi20.jar";
  String config = "CLDC-1.1";
  String configJar = "/lib/cldcapi11.jar";
  //Path to the bin folder of the Java installation
  String javaPath = "C:/Program Files/Java/jdk1.6.0/bin";

  int initialCleanupOK = 1;//Success = 0
  int compileOK = 1;//Compiler success = 0
  int preverifyOK = 1;//Preverify success = 0
  int deleteClassFilesOK = 1;//Delete success = 0
  int moveFilesOK = 1;//Move success = 0
  int manifestFileOK = 1;//Manifest success = 0
  int jarFileOK = 1;//Jar file success = 0
  int jadFileOK = 1;//Jad file success = 0
  int cleanupOK = 1;//Cleanup success = 0

  long jarFileSize = 0;

  JTextField progName;
  JTextField WTKroot;
  JTextField vendorText;
  JTextField midletVersionText;
  JTextField javaPathText;

  JRadioButton pButton10;
  JRadioButton pButton20;
  JRadioButton pButton21;

  JRadioButton cButton10;
  JRadioButton cButton11;
  //----------------------------------------------------//

  public static void main(String[] args){
    new WTKFramework01().new GUI();
  }//end main
  //----------------------------------------------------//

  void runTheProgram(){
    //This method is called when the user clicks the Run
    // button on the GUI.
    System.out.println("PROGRESS REPORT");
    System.out.println("Running program named: " + prog);

    //This code calls several methods in sequence to
    // accomplish the needed actions. If there is a
    // failure at any step along the way, the
    // framework will terminate at that point with a 
    // suitable error message.

    //Delete leftover files from a previous run, if any
    // exist
    deleteOldStuff();
    if(initialCleanupOK != 0){//Test for success
      System.out.println("Initial cleanup error");
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    compile();//compile the MIDlet
    if(compileOK != 0){//Test for successful compilation.
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    preverify();//Pre-verify the MIDlet class files
    if(preverifyOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Delete the class files from the original program
    // folder
    deleteClassFilesOK = deleteProgClassFiles();    
    if(deleteClassFilesOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Move the preverified files back to the original
    // program folder
    movePreverifiedFiles();
    if(moveFilesOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Make manifest file
    makeManifestFile();
    if(manifestFileOK != 0){
      System.out.println("Manifest file error");
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Make Jar file
    makeJarFile();
    if(jarFileOK != 0){
      System.out.println("JAR file error");
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Make Jad file
    makeJadFile();
    if(jadFileOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Delete extraneous files
    cleanup();
    if(cleanupOK != 0){
      System.out.println("Terminating");
      System.exit(1);
    }//end if

    //Run emulator
    runEmulator();

    //Reset success flags
    initialCleanupOK = 1;//Success = 0
    compileOK = 1;//Compiler success = 0
    preverifyOK = 1;//Preverify success = 0
    deleteClassFilesOK = 1;//Delete success = 0
    moveFilesOK = 1;//Move success = 0
    manifestFileOK = 1;//Manifest success = 0
    jarFileOK = 1;//Jar file success = 0
    jadFileOK = 1;//Jad file success = 0
    cleanupOK = 1;//Cleanup success = 0  

    //Control returns to here when the user terminates
    // the cell phone emulator.
    System.out.println(
      "\nClick the Run button to run another MIDlet.");
    System.out.println();//blank line

  }//end runTheProgram
  //----------------------------------------------------//

  //Purpose: Delete leftover files at startup
  void deleteOldStuff(){
    System.out.println(
           "Deleting leftover files from a previous run");

    //Delete subdirectory from output folder if it exists.
    int successFlag = deleteOutputSubDir();

    //Delete manifest file if it exists.
    File manifestFile = new File("output/Manifest.mf");
    if(manifestFile.exists()){
      boolean success = manifestFile.delete();
      if(success){
        System.out.println("   Manifest file deleted");
      }else{
        successFlag = 1;
      }//end else
    }//end if

    //Delete old JAR file if it exists.
    File jarFile = new File("output/" + prog + ".jar");
    if(jarFile.exists()){
      boolean success = jarFile.delete();
      if(success){
        System.out.println("   Old jar file deleted");
      }else{
        successFlag = 1;
      }//end else
    }//end if

    //Delete old JAD file if it exists.
    File jadFile = new File("output/" + prog + ".jad");
    if(jadFile.exists()){
      boolean success = jadFile.delete();
      if(success){
        System.out.println("   Old jad file deleted");
      }else{
        successFlag = 1;
      }//end else
    }//end if

    //Delete class files from program folder, if any exist
    int temp = deleteProgClassFiles();
    if(temp != 0){
      successFlag = temp;
    }//end if

    if(successFlag == 0){
      System.out.println("\nLeftover files deleted");
      initialCleanupOK = 0;//success
    }else{
    }//end else

  }//end deleteOldStuff
  //----------------------------------------------------//

  //This method compiles the MIDlet.  Note that the
  // compilation generates class files for a Java v1.4
  // virtual machine.  Apparently WTK2.5.2 is not
  // compatible with Java 1.6.  Also note that the
  // compiler uses the classes in the WTK2.5.2 library
  // JAR files instead of using the classes in the
  // J2SE v1.6 libraries.
  void compile(){
    try{
      String cmdString =
           javaPath + "/javac -target 1.4 -source 1.4 "
           + "-bootclasspath " + toolkit + configJar + ";"
           + toolkit + profileJar
           + " " + prog + "/" + prog + ".java";

      System.out.println("\nCompile command:");
      System.out.println(cmdString);
      Process proc = Runtime.getRuntime().exec(cmdString);

      //Delay until compilation is complete. Then test the
      // exit value of the compiler for success.  A value
      // of 0 indicates success.  Any other value
      // indicates that the compilation was not
      // successful.  Also note that this framework does
      // not display the compiler error messages.  If a
      // compilation fails, the next step is to use the
      // same approach from a batch file or from the
      // command line to perform the compilation on a
      // stand-alone basis.  That will expose the error
      // messages and allow for identifying and fixing the
      // problem in the MIDlet code.
      int val = proc.waitFor();
      compileOK = val;
      if(val == 0){
        System.out.println("Compile OK");
      }else{
        System.out.println("Compile error");
      }//end else

    }catch( Exception e ){
      e.printStackTrace();
    }//end catch

  }//end compile
  //----------------------------------------------------//

  //Purpose:  To preverify the class files.
  void preverify(){

    System.out.println(
        "\nPreverifying class files.");
    File dir = new File(prog);
    //Get a list of the files in the folder.
    String[] children = dir.list();
    for(int cnt = 0;cnt < children.length;cnt++){
      //Only preverify files with an extension of .class
      String aFile = children[cnt];
      if(aFile.endsWith(".class")){
        //This is a class file
        try{
          //Remove the .class extension from the file name
          aFile = aFile.substring(0,aFile.lastIndexOf(
                                               ".class"));

          String cmdString = 
                toolkit + "/bin/preverify.exe -classpath "
                + toolkit + configJar + ";"
                + toolkit + profileJar + " "
                + prog + "/" + aFile;

          System.out.println("\nPreverify command");
          System.out.println(cmdString);
          Process proc = Runtime.getRuntime().exec(
                                               cmdString);

          //Delay until preverification is complete
          int val = proc.waitFor();
          preverifyOK = val;
          if(val == 0){
            System.out.println("Pre-verify OK");
          }else{
            System.out.println("Pre-verify error");
            return;//return prematurely
          }//end else

        }catch( Exception e ){
          e.printStackTrace();
        }//end catch        
      }//end if
    }//end for loop
  }//end preverify
  //----------------------------------------------------//
  
  //Purpose: Move preverified files back to original
  // program folder to make it easier to put them into
  // a JAR file with the correct path.
  void movePreverifiedFiles(){

  System.out.println(
         "\nMoving preverified files to program folder.");
  File dir = new File("output/" + prog);
  //Get a list of the files in the folder.
  String[] children = dir.list();

  //Destination directory
  File dest = new File(prog);

  for(int cnt = 0;cnt < children.length;cnt++){
    String filename = children[cnt];

    try{
      // File to be moved
      String temp = "output/" + prog + "/" + filename;
      File file = new File(temp);

      // Move file to destination directory
      boolean success = file.renameTo(
                           new File(dest,file.getName()));
      if (!success){
        System.out.println("File move error");
        return;//return prematurely
      }else{
        System.out.println("   " + temp + " moved");
      }//end else

    }catch( Exception e ){
      e.printStackTrace();
     }//end catch
   }//end for loop
    
   moveFilesOK = 0;//Successful move
  }//end movePreverifiedFiles
  //----------------------------------------------------//

  void makeManifestFile(){
    try{
      BufferedWriter out = new BufferedWriter(
                    new FileWriter("output/Manifest.mf"));
      out.write("MIDlet-Name: " + prog + "\n");
      out.write(
               "MIDlet-Version: " + midletVersion + "\n");
      out.write("MIDlet-Vendor: " + vendor + "\n");
      out.close();
      System.out.println("\nManifest file written");
      manifestFileOK = 0;
    }catch( Exception e ){
      e.printStackTrace();
    }//end catch

  }//end makeManifestFile
  //----------------------------------------------------//

  void makeJarFile(){
    try{
      String cmdString = 
                    javaPath + "/jar cvfm output/" + prog 
                    + ".jar output/Manifest.mf ./" + prog;

      System.out.println("\nJAR command");
      System.out.println(cmdString);
      Process proc = Runtime.getRuntime().exec(cmdString);

      //Delay until complete
      int val = proc.waitFor();
      jarFileOK = val;
      if(val == 0){
        System.out.println("Jar file written");
      }else{
        System.out.println("Jar file error");
        return;//Return prematurely on error.
      }//end else

      //Get and save file size in bytes
      File file = new File("output/" + prog + ".jar");
      jarFileSize = file.length();
      System.out.println(
                     "   Jar file size = " + jarFileSize);

    }catch( Exception e ){
      e.printStackTrace();
    }//end catch

  }//end makeJarFile
  //----------------------------------------------------//

  void makeJadFile(){
    try{
      BufferedWriter out = new BufferedWriter(
               new FileWriter("output/" + prog + ".jad"));

      out.write("MIDlet-1: " + prog + ", , " + prog 
                                + "." + prog + "" + "\n");

      out.write("MIDlet-Name: " + prog + "\n");
      out.write("MIDlet-Version: " 
                                  + midletVersion + "\n");
      out.write("MIDlet-Vendor: " + vendor + "\n");
      out.write("MIDlet-Jar-URL: " + prog + ".jar\n");
      out.write("MIDlet-Jar-Size: " + jarFileSize + "\n");
      out.write("MicroEdition-Profile: " 
                                        + profile + "\n");
      out.write("MicroEdition-Configuration: " 
                                         + config + "\n");
      out.close();

      System.out.println("\nJad file written");
      jadFileOK = 0;
    }catch( Exception e ){
      e.printStackTrace();
    }//end catch
  }//end makeJadFile
  //----------------------------------------------------//

  //Purpose: To run Sun's cell phone emulator with this
  // MIDlet.
  void runEmulator(){
    try{
      String cmdString = toolkit + "/bin/emulator.exe "
                + "-Xdescriptor output/" + prog + ".jad";

      System.out.println("\nEmulator command");
      System.out.println(cmdString);
      Process proc = Runtime.getRuntime().exec(cmdString);

      //Delay until complete
      int val = proc.waitFor();
      if(val == 0){
        System.out.println("Emulator finished");
      }else{
        System.out.println("Emulator error");
      }//end else

    }catch( Exception e ){
      e.printStackTrace();
    }//end catch

  }//end runEmulator
  //----------------------------------------------------//

  //Purpose:  Delete all files and folders in the output
  // folder other than the jar and jad files. Also delete
  // all class files in the program folder.
  void cleanup(){
    //Delete subdirectory from output folder.
    int successFlag = deleteOutputSubDir();

    //Delete manifest file from output folder.
    File manifestFile = new File("output/Manifest.mf");
    if(manifestFile.exists()){
      boolean success = manifestFile.delete();
      if(success){
        System.out.println("   Manifest file deleted");
      }else{
        successFlag = 1;
      }//end else
    }//end if

    //Delete class files from program folder
    int temp = deleteProgClassFiles();
    if(temp != 0){
      successFlag = temp;
    }//end if

    if(successFlag == 0){
      System.out.println("\nExtraneous files deleted");
      cleanupOK = 0;//success
    }else{
    }//end else

  }//end cleanup
  //----------------------------------------------------//

  //Purpose: To delete the folder contained in the output
  // directory and all of its files.
  int deleteOutputSubDir(){
    int returnVal = 0;

    System.out.println(
                  "\nDeleting files from output folder.");
    //First delete the files in the subdirectory
    File subDir = new File("output/" + prog);
    //Get a list of the files in the folder.
    String[] children = subDir.list();
    if(children != null){
      for(int cnt = 0;cnt < children.length;cnt++){

        boolean success = (new File("output/" + prog + "/"
                               + children[cnt])).delete();
        if(!success){
          // Deletion failed
          returnVal = 1;
        }else{
          System.out.println(
                      "   " + children[cnt] + " deleted");
        }//end else
      }//end for loop
    }//end if !null

    //Now delete the subdirectory
    if(subDir.exists()){
      boolean success = subDir.delete();
      if(!success){
        // Deletion failed
        returnVal = 1;
      }else{
        System.out.println("   Empty directory named " 
                         + "output/" + prog + " deleted");
      }//end else
    }//end if

    return returnVal;
  }//end deleteOutputSubDir
  //----------------------------------------------------//

  //The purpose of this method is to delete the compiled
  // class files from the program folder.
  int deleteProgClassFiles(){
    int returnVal = 0;

    System.out.println(
        "\nDeleting class files from program folder.");
    File dir = new File(prog);
    //Get a list of the files in the folder.
    String[] children = dir.list();
    for(int cnt = 0;cnt < children.length;cnt++){

      //Don't delete files with extension of .java
      String aFile = children[cnt];
      if(aFile.indexOf(".java") < 0){
        //Not a .java file.
        boolean success = (
                   new File(prog + "/" + aFile).delete());
        if(!success){
          // Deletion failed
          returnVal = 1;
        }else{
          System.out.println("   " + aFile + " deleted");
        }//end else
      }else{
        //This is a .java file.
        System.out.println("   " + aFile + " saved");
      }//end else

    }//end for loop

    return returnVal;
  }//end deleteProgClassFiles
  //====================================================//

  //This is an inner class that controls the interactive
  // behavior of the framework.
  class GUI extends JFrame{

    GUI(){//constructor
      setSize(400,250);
      setTitle("Copyright 2007, R.G.Baldwin");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      JButton runButton = new JButton("Run");
      JPanel runPanel = new JPanel();
      runPanel.add(runButton);
      getContentPane().add(runPanel,"South");

      //Construct and populate fields for entry of text
      // data.
      JPanel textData = new JPanel();
      textData.setLayout(new GridLayout(0,2));
      getContentPane().add(textData,"North");

      //Populate first row of grid with default values.
      textData.add(new JLabel("  Program Name"));
      progName = new JTextField(prog);
      textData.add(progName);

      //Populate second row, etc.
      textData.add(new JLabel("  WTK Root"));
      WTKroot = new JTextField(toolkit);
      textData.add(WTKroot);

      //Populate third row ...
      textData.add(new JLabel("  Vendor"));
      vendorText = new JTextField(vendor);
      textData.add(vendorText);

      //Populate fourth row ...
      textData.add(new JLabel("  Midlet Version"));
      midletVersionText = new JTextField(midletVersion);
      textData.add(midletVersionText);

      //Populate fifth row
      textData.add(new JLabel(
                            "  Path to Java bin folder"));
      javaPathText = new JTextField(javaPath);
      textData.add(javaPathText);

      //Create column titles for profile and configuration
      // buttons
      textData.add(new JLabel(" "));//spacer
      textData.add(new JLabel(" "));//spacer
      textData.add(new JLabel("  Profile"));
      textData.add(new JLabel("  Configuration"));

      //Construct and populate radio buttons for
      // selection of profile and configuration.
      JPanel proCon = new JPanel();
      proCon.setLayout(new GridLayout(0,2));
      getContentPane().add(proCon,"Center");

      //Construct JPanel with radio buttons for selection
      // of profile.
      JPanel proButtons = new JPanel();
      proButtons.setLayout(new GridLayout(0,1));
      pButton10 = new JRadioButton("MIDP-1.0");
      pButton20 = new JRadioButton("MIDP-2.0",true);
      pButton21 = new JRadioButton("MIDP-2.1");

      //Make the buttons mutually exclusive.
      ButtonGroup profileGroup = new ButtonGroup();
      profileGroup.add(pButton10);
      profileGroup.add(pButton20);
      profileGroup.add(pButton21);

      //Add the radio buttons to the GUI
      proButtons.add(pButton10);
      proButtons.add(pButton20);
      proButtons.add(pButton21);
      proCon.add(proButtons);

      //Construct JPanel with radio buttons for selection
      // of configuration.
      JPanel configButtons = new JPanel();
      configButtons.setLayout(new GridLayout(0,1));
      cButton10 = new JRadioButton("CLDC-1.0");
      cButton11 = new JRadioButton("CLDC-1.1",true);

      //Make the buttons mutually exclusive.
      ButtonGroup configGroup = new ButtonGroup();
      configGroup.add(cButton10);
      configGroup.add(cButton11);

      //Add the radio buttons to the GUI
      configButtons.add(cButton10);
      configButtons.add(cButton11);
      proCon.add(configButtons);

      //Register an action listener on the Run button
      runButton.addActionListener(
        new ActionListener(){
          public void actionPerformed(ActionEvent e){
            //Get user inputs from text fields
            prog = progName.getText();
            toolkit = WTKroot.getText();
            vendor = vendorText.getText();
            midletVersion = midletVersionText.getText();
            javaPath = javaPathText.getText();

            //Set the profile based on which radio button
            // was selected by the user
            if(pButton10.isSelected()){
              profile = "MIDP-1.0";
              profileJar = "/lib/midpapi10.jar";
            }else if (pButton20.isSelected()){
              profile = "MIDP-2.0";
              profileJar = "/lib/midpapi20.jar";
            }else{//no other choice available
              profile = "MIDP-2.1";
              profileJar = "/lib/midpapi21.jar";
            }//end else

            //Set the configuration based on which radio
            // button was selected by the user
            if(cButton10.isSelected()){
              config = "CLDC-1.0";
              configJar = "/lib/cldcapi10.jar";
            }else{//no other choice available
              config = "CLDC-1.1";
              configJar = "/lib/cldcapi11.jar";
            }//end else

            //Now run the program.
            runTheProgram();

          }//end actionPerformed
        }//end new ActionListener
      );//end addActionListener

      setVisible(true);
    }//end constructor
  }//end class GUI
  //====================================================//
}//end class WTKFramework01


Copyright

Copyright 2008, Richard G. Baldwin. Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin's Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP). His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments. (TI is still a world leader in DSP.) In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel