Sample J2ME app using Eclipse / STS

I’m using Spring Tools Suite that’s based on Eclipse, which can be downloaded from here, but the process below should be the same in general if you use regular Eclipse. You’ll need STS not based on Eclipse Juno (see this for more info), so download STS 2.9.2, which is based on Eclipse 3.7.2.

For testing, I’ve been using MicroEmulator.

Configuring Eclipse

After getting the base Eclipse / STS distribution, install Mobile Tools for Java (MTJ) from here. There’s a good tutorial on eclipse.org itself on how to make the basic MIDlet and run it in the emulator.

Sample J2ME HTTP fetcher MIDlet

MIDlets are Java classes used as entry points to your application. You have three abstract methods to override:

  • destroyApp – called when the MIDlet is about to be destroyed
  • pauseApp – called when MIDlet is to be paused, so other MIDlet can run instead
  • startApp – called when MIDlet is to be activated (either for the first time or after being paused)

We’ll make a sample HTTP fetcher. I’ll go straight to the code. First, a helper class:

package com.icyrock.j2me.prb;

public interface IcyrockRunnable {
  public void run() throws Exception;
}

This one is the same as the regular java.lang.Runnable with the exception that it can throw Exceptions, just for convenience.

Main class

I’ll dissect the main class now – the whole class is pasted after this if you just want to copy & paste:

package com.icyrock.j2me.prb;

import java.io.InputStream;
import java.util.Hashtable;

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class HttpFetchPrb extends MIDlet {
  private Hashtable commands = new Hashtable();
  private Form form;
  private TextField urlTxt;
  private TextField responseTxt;

After some imports, there are 4 fields:

  • commands field is going to store command-to-IcyrockRunnable mapping. Easy for mapping commands
  • form is our top-level form, holding the other GUI controls
  • urlTxt and responseTxt are two GUI controls put on the form that we’ll use to store the URL to fetch from and the response itself (including the eventual errors)
  public HttpFetchPrb() {
    form = new Form(this.getClass().getName());
    form.setCommandListener(new CommandListener() {
      public void commandAction(Command cmd, Displayable arg1) {
        IcyrockRunnable toRun = (IcyrockRunnable) commands.get(new Integer(cmd.getCommandType()));
        if (toRun != null) {
          try {
            toRun.run();
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
    });

This is the start of the constructor. We create the form and then initialize the command listener. Command listener is a top-level listener for all commands put on this form. They are differentiated by the command type, which we then map through our commands field and run the required IcyrockRunnable.

    urlTxt = new TextField("URL:", "http://192.168.122.1/", 256, TextField.ANY);
    form.append(urlTxt);

    final int maxLen = 1024;
    responseTxt = new TextField("Response", "", maxLen, TextField.ANY);
    form.append(responseTxt);

We now create the text field holding the actual URL and the response text field. The default URL is put there – in my case, I put 192.168.122.1, but to test you should put the real IP of the machine you are running the emulator on. MicroEmulator has the option to allow MIDlets to access network – this should be enabled by default, but if not, the option is in Options menu / MIDlet Network access.

    int priority = 1;
    addCommand(new Command("Fetch", Command.OK, priority++), new IcyrockRunnable() {
      public void run() throws Exception {

This is how a command is created. You give it the name (“Fetch”) and the type (Command.OK, an int). addCommand method is just a helper method that you’ll see at the end.

        HttpConnection con = (HttpConnection) Connector.open(urlTxt.getString());
        try {
          int respCode = con.getResponseCode();

          String responseStr = "";
          int responseLen = (int) con.getLength();

          int len = responseLen > maxLen ? maxLen : responseLen;
          byte[] respData = new byte[len];

          InputStream ins = con.openInputStream();
          ins.read(respData);
          ins.close();

          responseStr = new String(respData);

          responseTxt.setString("Response code: " + respCode + "\n" + responseStr);
        } catch (Exception e) {
          responseTxt.setString("Error: " + e.getMessage());
        } finally {
          con.close();
        }
      }
    });

This is the gist of the HTTP fetcher. In steps it:

  • Creates the HttpConnection to the given URL
  • Gets the response code
  • Creates the response buffer to hold the response itself (and limits to maxLen because that’s the length of our text field)
  • Opens and reads the input stream
  • Converts that to string
  • Puts the response code and the response itself into the responseTxt text field

There’s a guard for exceptions that prints the exception itself into the response text field. Not sure if that’s the general stance, but in MicroEmulator that I used for testing, fetching the URL that doesn’t exist (i.e. when HTTP response is 404) throws an exception instead of giving a 404 response code.

    addCommand(new Command("Exit", Command.EXIT, priority++), new IcyrockRunnable() {
      public void run() throws Exception {
        destroyApp(false);
        notifyDestroyed();
      }
    });
  }

This command is an exit command. When deliberately exiting, you should destroy the resources and then call notifyDestroyed to notify the container that it wants to get destroyed.

  protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
  }

  protected void pauseApp() {
  }

  protected void startApp() throws MIDletStateChangeException {
    Display display = Display.getDisplay(this);
    display.setCurrent(form);
  }

Two required abstract method implementations – we don’t have anything to destroy, nor want to handle app pausing. startApp is important – here we take the display and slap our form on it, so it’s visible.

  private void addCommand(Command command, IcyrockRunnable icyrockRunnable) {
    commands.put(new Integer(command.getCommandType()), icyrockRunnable);
    form.addCommand(command);
  }
}

This is a helper method used above – it just records the command handler and puts it on the form.

Complete main class

Here’s the complete main class:

package com.icyrock.j2me.prb;

import java.io.InputStream;
import java.util.Hashtable;

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class HttpFetchPrb extends MIDlet {
  private Hashtable commands = new Hashtable();
  private Form form;
  private TextField urlTxt;
  private TextField responseTxt;

  public HttpFetchPrb() {
    form = new Form(this.getClass().getName());
    form.setCommandListener(new CommandListener() {
      public void commandAction(Command cmd, Displayable arg1) {
        IcyrockRunnable toRun = (IcyrockRunnable) commands.get(new Integer(cmd.getCommandType()));
        if (toRun != null) {
          try {
            toRun.run();
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
    });

    urlTxt = new TextField("URL:", "http://192.168.122.1/", 256, TextField.ANY);
    form.append(urlTxt);

    final int maxLen = 1024;
    responseTxt = new TextField("Response", "", maxLen, TextField.ANY);
    form.append(responseTxt);

    int priority = 1;
    addCommand(new Command("Fetch", Command.OK, priority++), new IcyrockRunnable() {
      public void run() throws Exception {
        HttpConnection con = (HttpConnection) Connector.open(urlTxt.getString());
        try {
          int respCode = con.getResponseCode();

          String responseStr = "";
          int responseLen = (int) con.getLength();

          int len = responseLen > maxLen ? maxLen : responseLen;
          byte[] respData = new byte[len];

          InputStream ins = con.openInputStream();
          ins.read(respData);
          ins.close();

          responseStr = new String(respData);

          responseTxt.setString("Response code: " + respCode + "\n" + responseStr);
        } catch (Exception e) {
          responseTxt.setString("Error: " + e.getMessage());
        } finally {
          con.close();
        }
      }
    });

    addCommand(new Command("Exit", Command.EXIT, priority++), new IcyrockRunnable() {
      public void run() throws Exception {
        destroyApp(false);
        notifyDestroyed();
      }
    });
  }

  protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
  }

  protected void pauseApp() {
  }

  protected void startApp() throws MIDletStateChangeException {
    Display display = Display.getDisplay(this);
    display.setCurrent(form);
  }

  private void addCommand(Command command, IcyrockRunnable icyrockRunnable) {
    commands.put(new Integer(command.getCommandType()), icyrockRunnable);
    form.addCommand(command);
  }
}

Result

Here’s how it looks in MicroEmulator: