Tuesday Oct 30, 2007

I came across a little challenge today when modifying an existing java collaboration in Java CAPS.

I'm calling an AccuWeather service to get a short-range forecast for a given location. In the reply, there's a small two-digit numerical element which refers to the icon which represents that day's weather. I need to take that icon and base64 encode it before sending it out of Java CAPS.

You can fetch the relevant image by constructing a URL quite easily but obviously I don't want to be doing this too often and I'd prefer to have a local cache of those images. But how to access them from a java collaboration?

The solution is simple when you know how to do it - it took a fair amount of searching (and a stupid bug of my own making) before I came up with an answer.

  1. Have all your images in a directory, then create a jar file with the contents of that directory. Mine are named 01.gif, 02.gif, ... and so on.
  2. Import the jar into your java collaboration.
  3. Access the image you need using getResourceAsStream().
  4. Read the bytestream.
  5. Perform the Base64 encoding.
Here's the relevant code snippet:
java.io.BufferedInputStream is = new java.io.BufferedInputStream(
            this.getClass().getResourceAsStream( "/WeatherImages/" +
                                                 iconNum +
                                                 ".gif" ) );
if (is != null) {
    java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
    byte[] buf = new byte[1024];
    int bytesRead = 0;
    while ((bytesRead = is.read( buf, 0, buf.length )) > 0) {
        bos.write( buf, 0, bytesRead );
    }
    sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
    String iconEncoded = encoder.encode( bos.toByteArray() );
    // Next line removes CRLFs which mobility pack Base64 decoder
    // doesn't seem to like    
    iconEncoded = iconEncoded.replaceAll( "\r|\n", "" );
    output.getForecastResponseType().getForecast( i1 ).setIcon( iconEncoded );
    is.close();
}

Wednesday Oct 24, 2007

One thing which has been bugging me about the mSOA Reference Architecture I'm working on for Sierra (aka Java CAPS 5.2) is that so far, it's been really tough for one MIDlet to invoke another.

We have one main controller MIDlet (the "Service Manager" or "Application Manager") which gets a list of viable applications from the backend servers -- Java CAPS, Directory Server and so on. The user should then be able to launch any one of the MIDlets at will.

The J2ME security model doesn't want you doing that.

Until now, we've been making do with everything being packaged into a single giant MIDlet. Some of the problems with this are that:

  1. It's not modular. It's one giant application. Ugh.
  2. No separation between MIDlets. All UI code will be in a single class file. If you have more than one developer, they'll be treading on each other's toes all the time.
  3. The flow design in the NetBeans mobility pack becomes impossible to follow after just one MIDlet. If you want to see how bad it is, this screenshot should give you an idea what it's like after just one application.
I don't claim to be a J2ME expert so it's taken a lot of searching for a portable way of doing this. The answer seems to be with the PushRegistry API which is part of the MIDP 2.0 specification. The MIDlets still need to be part of the same suite, which means they're still packaged into the same jar file and you need to use OTA (Over The Air) provisioning to make it work.

But work it does... take a look at the this project zip. It was built using NB 551 with the mobility pack and WTK 2.5.2. Run the Hello MIDlet in the emulator and when you press OK, it should then launch the Goodbye MIDlet.

This blog copyright 2009 by Adam Turnbull