Building a Connected Midlet
...and installing on a mobile device
One of the most powerful aspects of J2ME is connected mobility: you're no longer tied to your desk to accomplish many vital tasks. You can carry everything you need in your shirt pocket, send an e-mail while standing in line at the grocery store, or check the latest stock figures while at a baseball game.
There is nothing new about distributed applications; people have been using them for years and their effectiveness and potential are widely accepted. This is where J2ME comes into the picture - providing mobile devices with a powerful means of running robust connected applications. The possibilities seem endless.
We assume you're already familiar with J2SE or J2EE and the basic architecture of a J2ME MIDlet. If not, you might want to read our article "Building Your First J2ME Application" at www.altisimo.com/publications/j2me-hello.
This article shows how to build an Internet-enabled mobile application and illustrates the considerations that must be taken into account during design and development. The application we build consists of a J2ME client and a J2EE server. By the end of this article, you'll know how to:
The Echo Application
- Get input from a user
- Programmatically establish an Internet connection
- Send data to a remote server
- Read the data returned by the server
- Present the received data to the user
We'll first build an "Echo" client and server. Echo is the standard program we write when learning a new networking technology. The client sends a String to the server, the server sends it back to the client, and the client verifies that the server's response is the original String. This little application allows us to test many of the most important features of a new networking system: collecting and displaying information to a user, establishing a connection to a remote system, and transmitting and receiving data along that connection.
We'll build the application iteratively, improving it with each iteration. In the initial iteration, we're primarily interested in having the mobile device establish a connection to a remote server and using that connection to transmit and receive information. As a result, we need both J2EE and J2ME components, each of which is tailored for a specific set of tasks. The aspect of the application we're most interested in is the J2ME client (EchoClient), which runs on the mobile device.
The J2EE portion of the application (EchoServlet) is a rudimentary servlet that will accept an incoming request, read a string from that request, and then send the same string back to the mobile device. We have the servlet running on our Web site, so you don't need to do anything with J2EE if you don't wish to; the client code you download is configured to connect to our Web site. We only bring it up in the context of a J2ME application to show that the mobile phone needs to connect to something running on a remote server.
The application consists of:
- J2ME client: The client side of our application consists of a single class named EchoClient, which is installed on the mobile device. EchoClient initiates and uses a connection to EchoServlet. EchoClient is responsible for creating and displaying all the forms that allow the user to enter and read data that is sent to and received from EchoServlet.
- J2EE server: The server is a simple Web application that reads in a String from the client and returns the same String. The server side consists of these classes:
-EchoServlet: The servlet class, which reads in the String using readUTF, and then writes it back out using writeUTF.
-EchoTest: An automated test client to ensure that the servlet is functioning properly before we begin writing J2ME client code. For a more complex application, we would use JUnit; however, for this example, this simpler test is acceptable.
- Build tools: We use Ant and Antenna to compile the .java files, preverify the .class files, and update the version number and .jar size details in the .jad, as well as handle all the other tasks involved with building a J2ME application. We can run our most common tasks using single commands, thereby eliminating typos and unintentional changes to or deletions of files. Ant takes care of platform independence: all we have to do is write build (Unix shell script) and build.bat (Windows batch file) to launch build.xml from either development environment (Linux or Windows).
Testing the Servlet
We start by building a test of the server. It might seem backward to write a test of the server before we have a working server, but it saves time later. If we know the servlet works, we can be sure any problems we run into during later phases of development originate in the client. EchoTest is a simple custom HTTP client that connects to the EchoServlet, sends a string, and receives the servlet's response. It is useful as a command-line tool, and contains only a main method, which has the URL hard coded, as well as the String to be sent ("foo"):
URL enteredURL = newURL("http://altisimo.com/echo");
String toSend = "foo";
EchoTest uses this line of code to establish the connection to the servlet:
HttpURLConnection con = (HttpURLConnection)enteredURL.openConnection();
The main functions of EchoTest are:
The relevant code in EchoTest is shown in Listing 1.
- Make sure that EchoServlet can both receive and transmit information.
- Print the result to the screen.
Coding the Servlet
EchoServlet is a stripped down HttpServlet - all it does is read in a String using readUTF, and then return the same String using writeUTF. readUTF and writeUTF are methods associated with DataInputStream and DataOutputStream, respectively, and are used to read and write Strings across a network connection. readUTF and writeUTF are the methods to use when you're dealing with Strings, and there are specific read and write methods for most of the other data types available in MIDP. You can find a full list of these methods in the MIDP 1.0 API Javadoc.
The code in EchoServlet that does the work is in the doPost method (we delegate our doGet method to doPost, which is a pretty standard practice to simplify maintenance and reduce the number of "moving parts" in the servlet). doPost creates a DataInputStream to read data from the incoming HttpServletRequest, and a DataOutputStream to write data out to the corresponding HttpServletResponse. It collects the data from the client using readUTF, and writes it back out using writeUTF. It's important to set the content type to "application/octet-stream" because we're sending data (a UTF String), not simple text or HTML.
We are sending our response encoded as UTF; there's no readString or writeString method available in DataInputStream or DataOutputStream, so readUTF and writeUTF are the simplest ways to transmit Strings. We need to send the response as an octet-stream because UTF characters don't have a fixed size. They can be 1-3 bytes long, and so must be handled as data. It's also good form to make sure you flush the OutputStream at the end of the method, to be sure that everything you want to send gets out (see Listing 2).
The servlet binary .ear file and EchoTest source code are available for download from www.altisimo.com/download/echo.ear and www.altisimo.com/download/echo-1.0.zip so you can experiment with them if you wish. Typical usage should look like Figure 1.
The J2ME Client, Phase I
Now that we know the J2EE portion of the application is functioning correctly, it's time to develop our J2ME client. For this implementation we'll use a single-class solution, EchoClient.
Once again, we won't worry about collecting an input String from the user and will just send "foo" each time, as we did in EchoTest. At this point, we're concerned with making sure that the mobile device can establish the connection and send and receive data. We'll add a way for users to input a String of their choice later.
The main objectives in this section of the implementation are the same as in the development of EchoTest:
Opening a connection using J2ME is slightly different than in J2SE, so we decompose it into three smaller methods (openConnection, openConnectionOutputStream, and openConnectionInputStream) to make the code more readable and to easily allow for the substitution of different methods. The three methods are called when the client code executes outputEcho, which establishes the connection, sends and receives the data, and displays the results to the user. Detailed discussions of each of these methods follow the outputEcho in Listing 3.
- Establish the connection to the Echo server
- Display the results on the screen
The first thing we do in outputEcho is call openConnection, which connects to the URL specified and sets the User-Agent, Content-Type, and Request-Type:
String service URL = getAppProperty ("Echo-servlet-URL");
HttpConnection connection = (HttpConnection) Connector.open(serviceURL);
Next, outputEcho calls openConnectionOutputStream, which consists of a single line of code that returns the HttpConnection's DataOutputStream:
After that, outputEcho gets the HttpConnection's inputstream by calling openConnectionInputStream. There are some checks in openConnectionInputStream that were not required when opening the DataOutputStream. openConnectionInputStream's first task is to get an HTTP status code from the connection. The status code comes from the server (status codes are defined in RFC 2616). If the HTTP status code indicates that the URL will be redirected or has been redirected, then we cache the new URL to improve the performance of subsequent requests. If the status code indicates that our URL is valid, we return a DataInputStream; otherwise we print a message to system.out to tell the user that the DataInputStream could not be opened. In a truly robust program, this condition should bring up an error screen on the device to alert the user to the failure, but for now we'll be content with an error appearing in the emulator console (see Listing 4).
Once a successful connection has been established and the data has been both written from and received by the MIDlet, we construct a Form to display the result to the user. There's no way to simply print to the display, as we would have been able to do to System.out or System.err in J2SE, so we must use a Form. The Form contains only the String that was returned from the server. To build the Form, we first instantiate a new Form object with the title "Echo Output" and then append the returned String. Once we have placed the data in the Form, we add an Exit Command, which we created earlier as a class-scope variable, and assign a CommandListener, in this case the EchoClient class. Then we use display.setCurrent to display the new Form to the user.
Form outputForm = new Form("Echo Output");
The .jad File
J2ME applications are traditionally installed via a .jad file, a descriptor file that the mobile device uses to ensure that it has enough room to store and run the application. The .jad file for our Echo client looks like this:
MIDlet-Vendor: Altisimo Computing
The first six lines of the file are required in every .jad file, and are for the most part self-explanatory. The first line holds three pieces of information: (1) the name of the first MIDlet, (2) the icon to display in association with the MIDlet (in this case there is no icon, so it's left blank), and (3) which class to run in the .jar file to start the MIDlet. If there were more than one application in this MIDlet suite, we would include an additional line for each one, beginning with MIDlet-2, etc. It's important to ensure that the MIDlet-JAR-Size exactly matches the size of the .jar file, or devices will not be able to successfully download the application.
The last line in the .jad file, Echo-Servlet-URL, is one that we added. Like a J2EE deployment descriptor, we can use the .jad file to set some application properties outside of the code. A major advantage to putting the URL in the .jad file is that you're able to change the target of the application without having to recompile and repackage everything.
When users run the client, there's a short pause as it talks to the Echo server, and then they see a screen that looks like Figure 2. This figure shows that the Echo server sent the string "foo" back to the client. So far, so good.
The J2ME Client, Phase II
Now that we have successfully established and used a connection to a remote server, it's time to add some functionality for the users. First, we'll allow users to enter the String that they want to be echoed by the server. This is similar to what would be done if we were building a Swing-based GUI, although stripped down significantly. Once again, we use a Form to gather the information from the user. To get the user's input, we create a TextField and append it to the Form. A nice feature of TextField is that the label for the field is directly associated with the TextField, so they'll be rendered next to each other on the client device.
Form inputForm = new Form("Echo Input");
inputField = new TextField("String:", "", 10, TextField.ANY);
To get the String that the user entered, we call getString on the TextField. As a result, we change the first line of echoOutput to read as follows:
String fromUser = inputField.getString();
The input gathering and output screens look like Figures 3-5.
This is the only other change we need to make to allow the user to input a String to be echoed. To be thorough, we really should validate the String to make sure that it's not null or empty, since that will cause a RuntimeException, but we'll put this off until later.
Now that we have a working Echo client, we need to let our users install it. The easiest way to distribute an application is to make the .jad and .jar files available for download on a Web site. More often than not, a new user is going to download your application directly to a handheld device, not to a desktop PC, so you need to make your .jad and .jar files available on a site that is accessible to the device's very limited Web browser. Most devices have small displays, slow network connections, and limited HTML browsers, so make the page simple. By the time a user decides to download an application, they're interested in just that, not pretty pictures and other fluff.
This whole process of finding, downloading, and installing applications is called over the air (OTA) provisioning. Provisioning is jargon from the telecom world and is fairly synonymous with distribution or deployment; however, since the majority of the time we use mobile phones to accomplish this task, it seems fitting to use that industry's term.
You need to make one back-end change to your Web server for OTA provisioning to work correctly: you must update your Web server's configuration to serve up the .jad and .jar files as the correct file types. Optionally, you can use an OTA provisioning servlet that sets the MIME types for you, but we found it easier to just go in and make the change manually. We added a few lines to our Apache installation's conf/ httpd.conf file:
# For J2ME OTA provisioning:
AddType text/vnd.sun.j2me.app-descriptor jad
AddType application/java-archive jar
Once the .jad and .jar files are available and the changes have been made to your Web server configuration, all a user has to do is point the browser to the location of your .jad file and follow the prompts the device provides. A few clicks later and the application should be up and running on the user's device. Listing 5 provides a simple OTA provisioning Web page and Figure 6 provides a rendering of how it might look on your handheld browser.
That's it; you now have a connected MIDlet and all the information needed to install that MIDlet on a mobile device.
We have a working connected MIDlet, but this J2ME client is far from complete. In its current state, our client only works in the best case scenario and does not give the user the option to cancel a transaction once it has begun. There are a number of features we still need to implement, which we'll address in another article:
- Architecture: Our client is complex enough that we should be using multiple classes to handle the various tasks. We will rework the code to follow the architecture of Sun's J2ME blueprint.
- Progress indicator: We should tell users where we are in the communication process between the mobile device and the servlet. In the current implementation, they see a blank screen during the network round-trip and have no idea why it takes so long. We need to set users' expectations so they don't get angry at the application.
- Stop button: We should allow users to interrupt the connection. Users currently have no way to stop the network round-trip to check their calendar or do anything else on the handheld device.
- Input validation: The J2EE component of our application will throw an Exception if the String it receives is null or empty. We can resolve this issue by adding a preliminary client-side test before we send the data to the servlet.
- Exception handling: There are a variety of other issues that can arise during the execution of our application. We'll explore how to identify and handle many of these issues.
Alex Bourgeois is a cofounder of Altisimo Computing, a J2ME consulting and training company. He has been programming in Java since 1997 and has developed numerous applications for private and commercial use. Alex also cochairs the New England
Java Users Group J2ME Special Interest Group.
Richard Kasperowski, a cofounder of Altisimo Computing, has been programming in Java since 1997 and has developed numerous applications for private and commercial use. Richard is also the cochair of the New England Java Users Group J2ME Special Interest Group. [email protected]