Tuesday, March 15, 2011

JavaMe Tips: Where is FileConnection API writing my files when using Emulator?

Last weekend when working on a FileConnection API request I realized that I didn't know where in my PC were the emulator saving the files I was creating, so I made a little research...

I know there are different kind of emulators, some of them have their own way to simulate the behavior of the sd cards from mounting a user directory to the use of default directories in user's profile.

The Emulators I was using were WTK 2.5.2 and JavaMe sdk 3.0 and I was working on Netbeans 6.9.1 and here is what I found.

For WTK 2.5.2:
Try looking in 

<Documents and settings>/<UserName>/j2mewtk/2.5.2/appdb/<TempDir>/filesystem

Where <TempDir> is a temporal directory that is created to hold all the files in runtime. Be careful, everytime you close the emulator the dirs are deleted, so make any validation you need before closing your emulator.

For JavaMe sdk 3.0:
Try looking in

<DocumentsAndSettings>/<UserName>/javame-sdk/3.0/work/<EmNum>/appdb/filesystem

Where <EmNum> is a number representing the Emulator you are using, for example, DefaultCldcPhone1 emulator is number 6.

see ya soon!

References:

How to test file reading/writing and web server app in emulator? 2008. Forum.Nokia [online].
Available on Internet: http://discussion.forum.nokia.com/forum/showthread.php?143733-How-to-test-file-reading-writing-and-web-server-app-in-emulator
[accessed on March 13 2011].

Monday, March 14, 2011

Canvas ScreenShot in JavaMe

Due to the previous posts about image encoding and image's bytes, I found an interesting question about how to get a screenshot of a canvas in JavaMe and it turns out to be very simple. Just remember, once you get the mutable (changeable) Image you should encode it in some format (i.e. PNG, JPEG, etc...).

The following is a regular canvas class which paints a smily:

  
//imports...
public class MyCanvas extends Canvas {
    
    /**
     * Creates a mutable image representing a screenshot of the
     * canvas
     * @return Screenshot as a mutable image
     */
    public Image getScreenShot() {
      Image screenshot = Image.createImage(getWidth(), 
                         getHeight());
      Graphics g = screenshot.getGraphics();
      paint(g);
      return screenshot;
    }

    /**
     * Painting method
     * @param g Graphic object to which the painting is done
     */
    public void paint(Graphics g) {
      //change to black an paint the whole background
      g.setColor(0x000000);
      g.fillRect(0, 0, getWidth(), getHeight());

      //change to yellow
      g.setColor(0xFFFF00);
      //left|top position of the face so it seems centered
      int xCenter = (getWidth() - 80) / 2;
      int yCenter = (getHeight() - 80) / 2;
      //draw the face
      g.fillArc(xCenter, yCenter, 80, 80, 0, 360);
      //change to black color
      g.setColor(0x000000);
      //left eye
      g.fillArc(xCenter + 20, yCenter + 20, 10, 15, 0, 360);
      //right eye
      g.fillArc(xCenter + 50, yCenter + 20, 10, 15, 0, 360);
      //smile
      g.fillArc(xCenter + 15, yCenter + 50, 50, 10, 180, 180);

    }
  }

Notice that the +getScreenShot():Image method is creating a mutable image of the same width and height of the canvas. It also gets the Graphic object from that image so that the paint method can draw the smily on it.

What to do with the screenshot? As I told you before, it's up to you. You can convert it to bytes, save it using RMS or FileConnection API, send it over the network...etc. But whatever you choose to do, remember to encode it using PNG or JPEG encoders, otherwise your end user may not be able to see or manipulate the image.

see ya soon!

References:

A minimal PNG encoder for J2ME. 2009. [online].
Available on Internet: http://www.chrfr.de/software/midp_png.html
[accessed on March 12 2011].

J2ME Screenshot of a Canvas. March 2010. Forum.Nokia [online].
Available on Internet: http://discussion.forum.nokia.com/forum/showthread.php?191207-J2ME-Screenshot-of-a-Canvas
[accessed on March 12 2011].

PNG Encoding in Java ME. March 2010. Forum.Nokia [online].
Available on Internet: http://wiki.forum.nokia.com/index.php/PNG_Encoding_in_Java_ME
[accessed on March 12 2011].

Saturday, March 12, 2011

Convert image to byte array (byte[]) in JavaMe II

The Convert image to byte array (byte[]) in JavaMe post  showed how you can read an image from your jar file or from the sd card of your device and convert it to a byte array. Those images are called immutable (non-changeable) and when we read them we are reading bytes in some format (png, jpeg..) but, what happens when we are creating mutable (changeable) images in memory and we want to get their bytes?

For example, what if you can create a snapshot from what a canvas is showing? or what if you create an application that reads an image and apply some effects like blur, resizing, etc.?

You have to be very careful in these situations because you could, some how, get the bytes from the mutable image, but you certanly need to encode them (format them in png or jpeg...) in order to show them in your application or to write them to a file so the user can see it.

So in this post we are going to use a very simple PNG encoder. You can get more information about it using the following link:


There are some other encoders that also use compression and formats like bmp, jpeg, etc. but for this post we are just showing you an example with PNG as it is the MIDP's standard image format. Download the PNG class and add it to your project so you can use it.

In this example we are going to create a mutable image and then we are going to use some methods to get the image's bytes and finally we are going to encode those bytes using the PNG Encoder. The image is very simple, just a smily.



  
   //inside a midlet...

   /**
   * Creates a mutable (changeable) Image with a smily.
   * @return Image mutable image with a smile face on it
   */
  public Image createImage() {
    Image img = Image.createImage(100, 100);
    //Get the graphics so you can paint the image
    Graphics g = img.getGraphics();
    //change to black an paint the whole background
    g.setColor(0x000000);
    g.fillRect(0, 0, 100, 100);

    //change to yellow
    g.setColor(0xFFFF00);
    //left|top position of the face so it seems centered
    int xCenter = (img.getWidth() - 80) / 2;
    int yCenter = (img.getHeight() - 80) / 2;
    //draw the face
    g.fillArc(xCenter, yCenter, 80, 80, 0, 360);
    //change to black color
    g.setColor(0x000000);
    //left eye
    g.fillArc(xCenter + 20, yCenter + 20, 10, 15, 0, 360);
    //right eye
    g.fillArc(xCenter + 50, yCenter + 20, 10, 15, 0, 360);
    //smile...well kind of
    g.fillArc(xCenter + 15, yCenter + 50, 50, 10, 180, 180);

    return img;
  }

OK, with the last piece of code you created a mutable image (changeable) but, in what format is it? PNG? JPEG? none? That's correct you don't know, so that's why you have to encode it. The PNG encoder that we are using in this post has a method called: +toPNG(int,int,byte[],byte[],byte[],byte[]):byte[]
That's the method we are going to use in order to get our image's bytes in PNG format. But before using it, we need to get the params we are going to send. The first ints are the width and height of the image, the byte arrays are in order: alpha, red, green and... blue.

The width and height are pretty straightforward: +getWidth():int and +getHeight():int from the Image object, but to get the byte arrays you will need to do some binary operations:

  
   //inside a midlet...

  /**
   * Gets the channels of the image passed as parameter.
   * @param img Image
   * @return matrix of byte array representing the channels:
   * [0] --> alpha channel
   * [1] --> red channel
   * [2] --> green channel
   * [3] --> blue channel
   */
  public byte[][] convertIntArrayToByteArrays(Image img) {
    int[] pixels = new int[img.getWidth() * img.getHeight()];
    img.getRGB(pixels, 0, img.getWidth(), 0, 0, img.getWidth(), 
               img.getHeight());
    
    // separate channels
    byte[] red = new byte[pixels.length];
    byte[] green = new byte[pixels.length];
    byte[] blue = new byte[pixels.length];
    byte[] alpha = new byte[pixels.length];

    for (int i = 0; i < pixels.length; i++) {
      int argb = pixels[i];
      //binary operations to separate the channels
      //alpha is the left most byte of the int (0xAARRGGBB)
      alpha[i] = (byte) (argb >> 24);
      red[i] = (byte) (argb >> 16);
      green[i] = (byte) (argb >> 8);
      blue[i] = (byte) (argb);
    }

    return new byte[][]{alpha, red, green, blue};
  }

Don't you worry if you don't understand immediatly the above code, just imagine a pixel as the following int 0xAARRGGBB, where AA = alpha channel, RR = red channel, GG = green channel and BB = blue channel, so if you want to separate the alpha channel byte you need to move it to the right, that's the binary operator >> doing... and remember 1 byte = 8 bits so in order to move AA (1 byte = 8 bits) to BB you have to move it 3 times or 24 bits and when you have it in the right most byte, you can cast it to byte and then you have separated the alpha channel, got it?

Finally we need to invoke +toPNG(int,int,byte[],byte[],byte[],byte[]):byte[]  method in order to get our bytes encoded as PNG format and that's it. What to do with the encoded bytes is up to you... send them over the network, write them to a file using FileConnection API, etc. You can also check my previous posts if you need more documentation.

see ya soon!


References:

A minimal PNG encoder for J2ME. 2009. [online].
Available on Internet: http://www.chrfr.de/software/midp_png.html
[accessed on March 12 2011].

J2ME Screenshot of a Canvas. March 2010. Forum.Nokia [online].
Available on Internet: http://discussion.forum.nokia.com/forum/showthread.php?191207-J2ME-Screenshot-of-a-Canvas
[accessed on March 12 2011].

PNG Encoding in Java ME. March 2010. Forum.Nokia [online].
Available on Internet: http://wiki.forum.nokia.com/index.php/PNG_Encoding_in_Java_ME
[accessed on March 12 2011].

Saturday, February 26, 2011

Convert image to byte array (byte[]) in JavaMe

Last post showed you how to take a picture using the device's camera and then send it to a servlet in Base64 format. But sometimes, you just need to send a picture that is either inside the application or in your memory card (sd card, etc.).

So in this post, we are going to convert images inside the application to byte arrays so you can use the methods showed in the last post in order to send thouse images to the servlet. Also, we will see how to get byte arrays from a file with the help of the FileConnection API, but we are not getting in details with this API. I will share some links that make a great work for getting started with it.

The following method takes a String as parameter and looks inside the application to get the bytes:

  
     //inside MIDlet class...

   /**
   * Reads the local image resource and returns an
   * array of bytes representing the image
   * @param imgPath path to the image inside the app
   * @return array of bytes representing the image
   */
  public byte[] loadFromApp(String imgPath) {

    byte[] imgBytes = null;
    ByteArrayOutputStream baos = null;
    InputStream in = null;
    try {

      baos = new ByteArrayOutputStream();
      //this image is inside my mobile application
      in = this.getClass().getResourceAsStream(imgPath);
      byte[] buffer = new byte[1024];
      int n = 0;
      while ((n = in.read(buffer)) != -1) {
        baos.write(buffer, 0, n);
      }

      imgBytes = baos.toByteArray();

    } catch (Exception ex) {
      ex.printStackTrace();
    } finally {
      //whatever happends close the streams
      if (baos != null) {
        try {
          baos.close();
        } catch (Exception ex) {
        }
      }
      if (in != null) {
        try {
          in.close();
        } catch (Exception ex) {
        }
      }
    }

    return imgBytes;
  }

So you see it's pretty straightforward, just get the resource as a stream and start passing the bytes from one stream to another, at the end the ByteArrayOutputStream let you get the byte array.

But, what if you just want to send a picture you just take and is saved in your memory card (sd card, micro sd card...etc.)? well you have to access the file with the help of the FileConnection API. Here are some great links to help you get started with this API:

For getting started right from oracle:

For some considerations when using the API in real devices:

OK, after you understand how the API works and can access the files, you can use the following method to get the byte arrays of the files. You will realize that this method is very similar to the previous one:

  
     //inside MIDlet class...

   /**
   * Reads the image and returns an array of bytes
   * @param imgPath Path to the image inside the Device or
   * the memory card
   * @return array of bytes representing the image
   */
  public byte[] loadFromDevice(String imgPath) {

    byte[] imgBytes = null;
    ByteArrayOutputStream baos = null;
    InputStream in = null;
    FileConnection file = null;
    try {
      //Access to the file
      file = (FileConnection) Connector.open(imgPath);
      //Checks whether the file exist
      if (!file.exists()) {
        return new byte[0];
      }
      baos = new ByteArrayOutputStream();
      in = file.openInputStream();
      byte[] buffer = new byte[1024];
      int n = 0;
      while ((n = in.read(buffer)) != -1) {
        baos.write(buffer, 0, n);
      }

      imgBytes = baos.toByteArray();

    } catch (Exception ex) {
      ex.printStackTrace();
    } finally {
      //whatever happends close the streams
      if (baos != null) {
        try {
          baos.close();
        } catch (Exception ex) {
        }
      }
      if (in != null) {
        try {
          in.close();
        } catch (Exception ex) {
        }
      }
      if (file != null) {
        try {
          file.close();
        } catch (Exception ex) {
        }
      }
    }

    return imgBytes;
  }

There's one important thing to keep in mind with this API... not all devices implement it, so you should check if the device supports it before using it. The following method lets you check the availability of the API:

  
     //inside MIDlet class...

  /**
   * Checks whether the device has support for
   * the FileConnection API
   * @return true if and only if the device
   * supports the API false otherwise
   */
  private boolean checkFileSupport() {
    if (System.getProperty
       ("microedition.io.file.FileConnection.version") != null) {
      return true;
    }

    return false;
  }

I hope you can use this methods in your applications and start sharing some images!

see ya soon!


References:

Getting Started with the FileConnection APIs. December 2004. Oracle [online].
Available on Internet: http://developers.sun.com/mobility/apis/articles/fileconnection/
[accessed on February 26 2011].

Working with the Java ME FileConnection API on Physical Devices. March 2007. Java.net[online].
Available on Internet: http://today.java.net/article/2007/03/27/working-java-me-fileconnection-api-physical-devices
[accessed on February 26 2011].

Saturday, February 12, 2011

Taking a picture - Base64 encoding in JavaMe

It's been a while since my last post, January was a busy month, but here I am again.

In this post, we are going to do something really interesting: taking a snapshot with the phone's camera and send it to the server in Base64 format.

You should read the post about Base64 in JavaMe (J2ME) if you haven't:


Taking the snapshot is quite easy, you just have to be careful about blocking calls. The real problem happens when uploading the image to the server. I tried several scenarios using Glassfish 3.0.1 and Tomcat 6.0.32 and none of the scenarios worked on both servers... It was very frustrating becasue the code seems to be OK, but works for Glassfish or for Tomcat but not both.

Let's start with the general code, assume the following is inside the MIDlet class:

  
     //inside MIDlet class...

     /** Commands to control the application flow*/
     private Command commandShoot = null;
     private Command commandExit = null;
     private Command commandSend = null;
     private Command commandBack = null;
     private Display display = null;

     /** flags to indicate whether the phone 
      *  supports png or jpeg encoding for snapshots
      */
     private boolean png = false;
     private boolean jpeg = false;

     /** Canvas where the video is going to be played*/
     private VideoCanvas videoCanvas = null;

     /** Canvas where the snapshot is going to be shown*/
     private ImageCanvas imgCanvas = null;


     public void startApp() {
        display = Display.getDisplay(this);

        commandExit = new Command("Exit", Command.EXIT, 0);
        commandShoot = new Command("Shoot", Command.SCREEN, 0);
        commandSend = new Command("Send", Command.SCREEN, 0);
        commandBack = new Command("Back", Command.BACK, 0);

        //Verifies if the application can use the camera
        //and encoding
        if (checkCameraSupport() && 
            checkSnapShotEncodingSupport()) {

            //this is the canvas where the video will render
            videoCanvas = new VideoCanvas(this);
            videoCanvas.addCommand(commandExit);
            videoCanvas.addCommand(commandShoot);
            videoCanvas.setCommandListener(this);

            display.setCurrent(videoCanvas);
        } else {
            System.out.println("NO camera support");
        }
    }

    /**
     * Checks whether the phone has camera support
     * @return true if and only if the phone has camera
     * support
     */
    private boolean checkCameraSupport() {
        String propValue = System.getProperty
                           ("supports.video.capture");
        return (propValue != null) && propValue.equals("true");
    }
    
    /**
     * Checks if the phone has png or jpeg support.
     * @return true if and only if the phone supports
     * png or jpeg encoding
     */
    private boolean checkSnapShotEncodingSupport() {
        String encodings = System.getProperty
                           ("video.snapshot.encodings");
        png = (encodings != null) 
              && (encodings.indexOf("png") != -1);

        jpeg = (encodings != null) 
               && (encodings.indexOf("jpeg") != -1);

        return png || jpeg;
    }

The previous methods tell you if the phone has camera support and if it can encode snapshots in either png or jpeg format. The +startApp():void method, starts the application, checks for the camera support and starts the canvas where the video will render.

Next is the method that actually encodes an array of bytes. Later we will show how to pass the Image as a byte array to this method in order to get the Image in Base64 format:

  
     //inside MIDlet class...

    /**
     * Encodes an array of bytes
     * @param imgBytes Array of bytes to encode
     * @return String representing the Base64 format of
     * the array parameter
     */
    public String encodeImage(byte[] imgBytes) {
        byte[] coded = Base64.encode(imgBytes);
        return new String(coded);
    }

As we saw in the Base64 encode-decode in JavaMe (J2ME) Post, the previous method encodes using the bouncy castle library.

The following methods goes inside the MIDlet class as well, they are responsible of starting the canvas that takes the snapshot and passing the snapshot to another canvas in order to show it to the user:

  
    //inside MIDlet...

    /**
     * Starts the Video Canvas to take the snapshot
     */
    public void snapShot() {

        if (png) {
            videoCanvas.startSnapShot("png");
        } else if (jpeg) {
            videoCanvas.startSnapShot("jpeg");
        } else {
            videoCanvas.startSnapShot(null);
        }
    }

    /**
     * Shows the snapshot in a Canvas
     * @param bytes Array of bytes representing the
     * snapshot
     */
    public void showSnapShot(byte[] bytes) {
        if (bytes != null) {

            imgCanvas = new ImageCanvas(bytes);
            imgCanvas.addCommand(commandSend);
            imgCanvas.addCommand(commandBack);
            imgCanvas.setCommandListener(this);

            display.setCurrent(imgCanvas);
        }
    }

OK, that's pretty much the general code for the MIDlet, let's see what happens within the VideoCanvas class. The next code should be inside the constructor, it starts the video player:

        
    //inside VideoCanvas class...

    /* Application MIDlet**/
    ImageCaptureMidlet midlet = null;

    /** Control for the video*/
    VideoControl videoControl = null;

    public VideoCanvas(ImageCaptureMidlet mid) {
        midlet = mid;
        Player player = null;

        try {
            player = Manager.createPlayer("capture://video");
            player.realize();
            videoControl = (VideoControl) player.getControl
                           ("VideoControl");
            videoControl.initDisplayMode
                       (VideoControl.USE_DIRECT_VIDEO, this);
            videoControl.setDisplayFullScreen(true);
            videoControl.setVisible(true);

            player.start();

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

So, when the canvas is created it starts the player and the video starts playing inside the canvas. When the MIDlet invokes the +snapShot():void method, the following piece of code is executed inside the VideoCanvas class:

    //inside VideoCanvas class...

    /**
     * Starts a thread to take the snapshot. 
     * Some devices will take the snapshot without 
     * the need of a thread, but some others 
     * doesn't (including my emulator). 
     * So start a new Thread...
     * @param encoding String representing the encoding
     * to use when taking the snapshot
     */
    public void startSnapShot(final String encoding) {
        new Thread(new Runnable() {

            public void run() {
                try {
                    byte[] rawBytes = null;
                    if (encoding != null) {
                        //take the snapshot using the encoding
                        rawBytes = videoControl.getSnapshot
                                   ("encoding=" + encoding);

                    } else {
                        //take the snapshot using the best
                        //possible encoding. 
                        //Implementation dependent
                        rawBytes = videoControl.getSnapshot
                                   (null);
                    }
                    //ask the midlet to show the snapshot
                    midlet.showSnapShot(rawBytes);
                } catch (MediaException ex) {
                    ex.printStackTrace();
                }
            }
        }).start();
    }

You may notice that the method starts a new Thread. That's because not all devices will take the snapshot right away, some times it takes a little bit to start the camera and take the sanpshot. That's why, we have started a new Thread. Also, check that +VideoControl.getSnapshot(String):byte[] method can receive a null parameter, indicating that let the implementation decide which is the best encoding possible to take the snapshot. Finally, when the snapshot is taken, the method asks the application MIDlet to show it.

We have previously seen the +showSnapShot(byte[]):void method in the MIDlet class, so let's take care about the ImageCanvas class where the snapshot is shown:

    //inside ImageCanvas class...

    /** Snapshot to render*/
    private Image image = null;

    /** bytes of the snapshot*/
    private byte[] imageBytes = null;

    public ImageCanvas(byte[] bytes) {
        imageBytes = bytes;

        //creates an Image using the byte array 
        image = Image.createImage(imageBytes, 0, 
                                  imageBytes.length);
    }

    public void paint(Graphics g) {
        int width = getWidth();
        int height = getHeight();
        g.setColor(0x000000);
        g.fillRect(0, 0, width, height);

        //render the snapshot
        g.drawImage(image, getWidth() / 2, getHeight() / 2, 
                    Graphics.HCENTER | Graphics.VCENTER);
    }

    //Getters and Setters...


OK, up until now we have seen how to take a snapshot and render it on a Canvas. That's the general code we were talking about at the beginning of this post. Now let's see how to send the snapshot to the server. Here we are going to present two scenarios, one for Glassfish server and another for the Tomcat server.

Glassfish Server:
The following method is the method inside the MIDlet that sends the snapshot to a servlet deployed on Glassfish server 3.0.1:

    //inside MIDlet class

    /**
     * Sends the snapshot to the server
     */
    public void send() {
        new Thread(new Runnable() {

            public void run() {

                String imageEncoded = 
                   encodeImage(imgCanvas.
                               getImageBytes());                

                String format = png ? "png" : jpeg?"jpeg":"";
                
                //This is my servlet’s URL
                String URL = 
                           "http://localhost:8080/" + 
                           "Base64ExampleServlet_v2/" +
                           "ImageServlet";

                HttpConnection http = null;
                OutputStream os = null;
                DataOutputStream dout = null;
                try {
                    //Open HttpConnection using POST
                    http = (HttpConnection) Connector.open(URL);
                    http.setRequestMethod(HttpConnection.POST);
                    //Content-Type is must to pass parameters 
                    //in POST Request
                    http.setRequestProperty(
                         "Content-Type", 
                         "application/x-www-form-urlencoded");
                   
                    os = http.openOutputStream();

                    ByteArrayOutputStream bout = new 
                                       ByteArrayOutputStream();
                    dout = new DataOutputStream(bout);
                    dout.writeUTF(imageEncoded);
                    dout.writeUTF(format);
                    os.write(bout.toByteArray());
                    
                    os.flush();  
                } catch (IOException ex) {
                    ex.printStackTrace();
                } 
                //whatever happens, close the streams 
                //and connections
                finally {
                    if (os != null) {
                        try {
                            os.close();
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                    if (dout != null) {
                        try {
                            dout.close();
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                    if (http != null) {
                        try {
                            http.close();
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                }
            }
        }).start();
    }

As you can see, the method calls the encoding method passing the snapshot as a byte array, then it starts a HttpConnection to the servlet deployed on Glassfish server. It opens the OutputStream and writes the image as Base64 format and it also writes the encoding (format) used. On the server side, you should read the stream the same way you wrote it, taht is, first read the image in Base64 format and then the encoding used (format).

Tomcat Server:
The following method is the method inside the MIDlet that sends the snapshot to a servlet deployed on Tomcat server 6.0.32:

    //inside MIDlet class

    /**
     * Sends the snapshot to the server
     */
    public void send() {
        new Thread(new Runnable() {

            public void run() {

                String imageEncoded = 
                   encodeImage(imgCanvas.
                               getImageBytes());                

                String format = png ? "png" : jpeg?"jpeg":"";
                
                //This is my servlet’s URL
                String URL = 
                           "http://localhost:8080/" + 
                           "Base64ExampleServlet_v2/" +
                           "ImageServlet";

                HttpConnection http = null;
                OutputStream os = null;
                try {
                    //Open HttpConnection using POST
                    http = (HttpConnection) Connector.open(URL);
                    http.setRequestMethod(HttpConnection.POST);
                    //Content-Type is must to pass parameters 
                    //in POST Request
                    http.setRequestProperty(
                         "Content-Type", 
                         "application/x-www-form-urlencoded");
                   
                    os = http.openOutputStream();

                    //IMPORTANT, when writing Base64 format
                    //there are chars like '+' that  
                    //must be replaced when sending.
                    imageEncoded = 
                        imageEncoded.replace('+', '-');
                    StringBuffer params = new StringBuffer();
                    params.append("image" + "=" + imageEncoded);
                    params.append("&" + 
                                  "format" + "=" + format);
                    System.out.println(params.toString());
                    os = http.openOutputStream();
                    os.write(params.toString().getBytes());
                    
                    os.flush();  
                } catch (IOException ex) {
                    ex.printStackTrace();
                } 
                //whatever happens, close the streams 
                //and connections
                finally {
                    if (os != null) {
                        try {
                            os.close();
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                    if (http != null) {
                        try {
                            http.close();
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                }
            }
        }).start();
    }

This method differs from the Glassfish method in the way it writes the parameters. Tomcat's method writes the parameters as a key/value pair. Notice one important point, the Base64 format uses chars like '+', chars that may be confused in the HTTP protocol, because they can be translated as a space char. So before sending the image in Base64, replace the '+' chars with '-' chars, but when receiving in the servlet (server side), convert the chars back before writing the image to the disk. This is only needed in the Tomcat's method.

Wow, loooooong post... Again, my advise is be careful when deciding which method to use in which server. Sometimes the code you wrote may be well written but for some reasons it won't work on the server... so you have to try another way to write your code until it works.

That's it for now, hope this helps you to write applications that use the device's camera.

see ya soon!


References:

Taking Pictures with MMAPI. 2011. Oracle [online].
Available on Internet: http://developers.sun.com/mobility/midp/articles/picture/
[accessed on February 01 2011].

Java Tips - Capturing Video on J2ME devices. 2011. Java Tips [online].
Available on Internet: http://www.java-tips.org/java-me-tips/midp/capturing-video-on-j2me-devices.html
[accessed on February 01 2011].

J2ME Sample Codes: Image Capturing in J2ME. 2011. J2ME Sample Codes [online].
Available on Internet: http://j2mesamples.blogspot.com/2009/06/image-capturing-in-j2me.html
[accessed on February 01 2011].

Embedded Interaction - Working with J2ME - Picture transmission over HTTP. 2011. Embedded Interaction [online].
Available on Internet: http://www.hcilab.org/documents/tutorials/PictureTransmissionOverHTTP/index.html
[accessed on February 01 2011].

Base64. 2010. Wikipedia [online].
Available on Internet: http://en.wikipedia.org/wiki/Base64
[accessed on December 28 2010].

The Legion of the Bouncy Castle. bouncycastle.org [online].
Available on Internet: http://www.bouncycastle.org/
[accessed on December 28 2010].

Sunday, January 9, 2011

Servlet-GSON vs JSONME-JavaME III

This is the third post about JSON and JavaME. In previous releases: first we introduced JSON for JavaMe (J2ME), then we improved the example by adding compression/decompression support. Now, we are going to introduce Base64 format support for sending images in our JSON example.
Although this is not a best practice, because you should send the image's URL  instead of sending the image itself in the JSON String, sometimes you just need to do exactly that.

Requirements:

Servlet-GSON vs JSONME-JavaME: Contains the original classes. More info:
http://www.java-n-me.com/2010/11/servlet-gson-vs-jsonme-javame.html

Servlet-GSON vs JSONME-JavaME II: Second part of the saga. Adds compression/decompression support. More info:
http://www.java-n-me.com/2010/12/servlet-gson-vs-jsonme-javame-ii.html

Base64 encode-decode in JavaMe: Explains how to use Base64 format in JavaMe (J2ME). More info:
http://www.java-n-me.com/2010/12/base64-encode-decode-in-javame.html


You should check the previous requirements if you haven't, because in this post we use some of the classes defined there.

The image we are going to send is in PNG format:



OK, let's begin, first we are going to show you the changes in some of the original classes. As usual, let's start with the server side:

import java.awt.Image;
public class Client {

   /** Name of the client*/
    private String name;

    /** Last name of the client*/
    private String lastName;

    /** Identification of the client*/
    private String id;

    /** Photo of the client*/
    private transient Image photo;


    /** Photo of the client in Base 64 format*/
    private  String photoBase64;


    //Getters and Setters...
}

Our Client class has now two new attributes for the photograph of the client. The java.awt.Image attribute binds to the photograph of the client directly. The String attribute photoBase64 contains the Base64 format of the photograph that is going to be sent in the JSON String. Notice that the attribute photo is marked as transient, meaning that GSON won't use this attribute in the JSON String.

Following is the servlet class which will create a client with a photograph, will get the JSON String representation of the client and his photograph and will compress the data before sending the response to the mobile client. The servlet uses the GSON API, the java.util.zip.GZIPOutputStream and the JSE sun.misc.BASE64Encoder class:


import com.google.gson.Gson;
import java.io.*;
import java.net.URL;
import java.util.Vector;
import java.util.zip.GZIPOutputStream;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.*;
import javax.swing.ImageIcon;
import model.Client;
import sun.misc.BASE64Encoder;

public class ClientsServlet extends HttpServlet {
   ...

    /**
     * Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //sets the type of response and the charset used
        //Be careful, in your mobile client, read the response using the same charset
        //if sending the response as text. If you sent it as binary data,
        //there is no problem
        response.setContentType("application/json; charset=UTF-8");

        //create some clients and add them to the vector
        Vector vClients = new Vector();

        //used to convert Image to Base64 format
        BASE64Encoder encoder = new BASE64Encoder();

        Client clientOne = new Client();
        clientOne.setName("Alexis");
        clientOne.setLastName("López Ñ");
        clientOne.setId("123456");
        clientOne.setPhoto(new ImageIcon("images/duke.png").getImage());
        //get the image bytes. Read it as an URL
        byte[] imgBytes =
          getImageBytes("http://localhost:8080/GSON_Servlet/images/duke.png");
        String imageCoded = encoder.encode(imgBytes);
        clientOne.setPhotoBase64(imageCoded);

        //... add more clients...
      
        vClients
           .add(clientOne);

        //convert the clients vector to JSON using GSON, very Easy
        Gson gson = new Gson();
        String jsonOutput = gson.toJson(vClients);

        System.out.println("*****JSON STRING TO RESPONSE*****");
        System.out.println(jsonOutput);
        System.out.println("*********************************");

        System.out.println("Length of String in bytes = "
                                    + jsonOutput.getBytes().length);
        //compress de data
        byte[] compressed = compress(jsonOutput);
        System.out.println("Length of compressed bytes = "
                                    + compressed.length);

        //send the binary response
        ServletOutputStream out = response.getOutputStream();
        out.write(compressed, 0, compressed.length);
        out.flush();
        out.close();
    }
    ...
}

The servlet uses two important methods. One is the +compress(String):byte[] that we have analized in the previous post of this saga. The other one is the +getImageBytes(String):byte[] which is shown next:

/** * Returns the binary representation of the image passed as an URL * @param imgURL URL String of the image * @return array of binary data representing the image * @throws IOException */ public static byte[] getImageBytes(String imgURL) throws IOException { URL imgUrl = new URL(imgURL); ByteArrayOutputStream bout = new ByteArrayOutputStream(); InputStream in = imgUrl.openStream(); int i = 0; while ((i = in.read()) != -1) { bout.write(i); } in.close(); bout.close(); byte[] bytes = bout.toByteArray(); return bytes; }

The last method reads the image from the URL and gets its binary representation. One important thing to keep in mind, is that you should use PNG Images as it is the default format for JavaMe (J2ME) MIDP applications, not doing so may arise exceptions on the mobile client when parsing the Image data. When the servlet is run and it receives a POST request, the following message is shown on the console output:

INFO: *****JSON STRING TO RESPONSE***** 9/01/2011 06:11:23 PM INFO: [{"name":"Alexis","lastName":"López Ñ","id":"123456","photoBase64":"iVBORw0KGgoAAAANSUhEUgAAAP8AAADICAMAAAAQqcftAAAAAXNSR0IArs4c6QAAAwBQTFRFDAAA\r\nCgQKGAAAEAgMGAAIGAAQGAgIGAgQEBAIABAQCBAQEBAQEBAYEBgQEBgYECEYGAgYGBAIGBAQGBAY\r\nGBgIGBgQGBgYGBghGCEYIQAAIQAIIQAQIQgQIQgYIRAQIRAYKQAMKQgQKQAYIRAhIRgQIRgYIRgh\r\nISEYMQAILgUVNwQSQgAYKRAUKRgYKSEYPA0YISEhKRAhKRghKRgpMRQhMRgpPQghPRQhKSEhKSkh\r\nISEpKSYrMSUlNi4pOScvOzU1SBwpRkI9WgYgbBk1RkJKTkdHVkhNVFJMUlJaWlRUXlhaZVtdb2Fk\r\ncWxne2lwf3h5yBUsqDBU1Bcx0SRJ3BU53xs53yJK4TJjin5"
... not all data shown ... "}] INFO: *********************************



INFO: Length of String in bytes = 17425
INFO: Length of compressed bytes = 12890

As you can see, the JSON representation of the client now has the photograph in Base64 format in order to send the image data to the mobile client as part of the JSON String. Also notice that there is some compression in the response to the client.

On the mobile client side, there are also changes in the Client class:

import java.util.Vector;
import javax.microedition.lcdui.Image;
import org.bouncycastle.util.encoders.Base64;
import org.json.me.JSONArray;
import org.json.me.JSONException;
import org.json.me.JSONObject;

public class Client {


    /** Name of the client*/
    private String name;

    /** Constant name of the attribute name*/
    private static final String ATT_NAME = "name";

    /** Last name of the client*/
    private String lastName;

    /** Constant name of the attribute last name*/
    private static final String ATT_LAST_NAME = "lastName";

    /** Identification of the client*/
    private String id;

    /** Constant name of the attribute id*/
    private static final String ATT_ID = "id";

    /** Photo of the client*/
    private  Image photo;

    /** Photo of the client in Base 64 format*/
    private  String photoBase64;


     /** Constant name of the attribute photoBase64*/
    private static final String ATT_PHOTO_BASE_64 = "photoBase64";

    //Getters and Setters...

    /**
     * This method should be used by this class only, that's why it is private.
     * Allows to get a JSONObject from the Client passed as parameter
     * @param client Client Object to convert to JSONObject
     * @return JSONObject representation of the Client passed as parameter
     */
    private static JSONObject toJSONObject(Client client) {
        JSONObject json = new JSONObject();
        try {
            json.put(ATT_NAME, client.getName());
            json.put(ATT_LAST_NAME, client.getLastName());
            json.put(ATT_ID, client.getId());
            //only if the client has a photo associated send it as JSON
            if(client.getPhotoBase64() != null)
            {
                json.put(ATT_PHOTO_BASE_64, client.getPhotoBase64());
            }
        } catch (JSONException ex) {
            ex.printStackTrace();
        }
        return json;
    }

    /**
     * Allows to get a Client Object from a JSON String
     * @param jsonText JSON String to convert to a Client Object
     * @return Client Object created from the String passed as parameter
     */
    public static Client fromJSON(String jsonText) {
        Client client = new Client();
        try {
            JSONObject json = new JSONObject(jsonText);

            //check if the JSON text comes with the attribue Name
            if (json.has(ATT_NAME)) {
                //asign the String value of the attribute name to the client
                client.setName(json.getString(ATT_NAME));
            }

            //check if the JSON text comes with the attribue last name
            if (json.has(ATT_LAST_NAME)) {
                client.setLastName(json.getString(ATT_LAST_NAME));
            }

            //check if the JSON text comes with the attribue id
            if (json.has(ATT_ID)) {
                client.setId(json.getString(ATT_ID));
            }

             //check if the JSON text comes with the attribue photo
            if (json.has(ATT_PHOTO_BASE_64)) {
                client.setPhotoBase64(json.getString(ATT_PHOTO_BASE_64));


                //once the photo in base 64 format has been read,
                //convert it to an Image. IMPORTANT: PNG image format or
                //you may get an Exception
                byte[] decoded = Base64.decode(client.getPhotoBase64());
                Image img = Image.createImage(decoded, 0, decoded.length);
                client.setPhoto(img);
            }
        } catch (JSONException ex) {
            ex.printStackTrace();
        }
        return client;
    }
}

You can find the changes in bold. The important changes are the adition of the attributes for managing the photograph and the modification of the methods for parsing the JSON String to and from an Object. Notice the use of the org.bouncycastle.util.encoders.Base64 class to decode the Base64 format data of the photograph. You can find the original source code of the Client class (mobile client side) in the first post of this saga.

Next is the MIDlet that runs the code:

import com.tinyline.util.GZIPInputStream;
import java.io.*;
import java.util.Vector;
import javax.microedition.io.*;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.midlet.*;


public class JSONMIDlet extends MIDlet {


    private Form f = null;


    public JSONMIDlet() {
        //form for the photos
        f = new Form("Photo testing");
    }


    public void pauseApp() {
    }


    public void destroyApp(boolean unconditional) {
        notifyDestroyed();
    }


    public void startApp() {
        try {
            //ask for the clients
            Vector clients = getClients();


            //show information of the clients
            System.out.println("*****CLIENT INFORMATION*****");
            for (int i = 0; i < clients.size(); i++) {
                Client cl = (Client) clients.elementAt(i);


                System.out.println("Client " + (i + 1) 
                                                + " name = " + cl.getName());
                System.out.println("Client " + (i + 1) 
                                                + " last name = " + cl.getLastName());
                System.out.println("Client " + (i + 1) 
                                                + " ID = " + cl.getId());
                System.out.println("Client " + (i + 1) 
                                                + " Photo Base64 = " + cl.getPhotoBase64());
                System.out.println("");


                //if the client has a photo, append it to the form
                if (cl.getPhoto() != null) {
                    f.append(cl.getPhoto());
                }
            }




            Display dsp = Display.getDisplay(this);
            dsp.setCurrent(f);




        } catch (IOException ex) {
            //manage the exception
            ex.printStackTrace();
        }
    }


...//other methods


}

The method +getClients():Vector has not changed and can be found in the last post of this saga (as there is compression/decompression support). Finally the emulator shows the following:


Well, that's it for today. Quite a large post, I hope I was clear enaugh, but if you have any questions do not hesitate to contact me.

see ya soon!


References:

TinyLine Utils. 2010. TinyLine [online].
Available on Internet: http://www.tinyline.com/utils/index.html
[accessed on December 11 2010].

Using JavaScript Object Notation (JSON) in Java ME for Data Interchange. Agosto 2008. Oracle [online].
Available on Internet: http://java.sun.com/developer/technicalArticles/javame/json-me/
[accessed on October the 26th 2010].

meapplicationdevelopers: Subversion. 2007. Oracle [online].
Available on Internet: https://meapplicationdevelopers.dev.java.net/source/browse/meapplicationdevelopers/demobox/mobileajax/lib/json/
[accessed on October the 27th 2010].

The Legion of the Bouncy Castle. bouncycastle.org [online].
Available on Internet: http://www.bouncycastle.org/
[accessed on December 28 2010].

Tuesday, December 28, 2010

Base64 encode-decode in JavaMe

I want to start this post asking: why do you think you need to encode data? At first, I didn't have a specific answer, so I googled and found some interesting answers and now I can tell you: You want to encode, because you need to transfer binary data through a channel that is designed to deal with textual data. A common example is when you need to transfer an Image inside a JSON String or in a XML message.

We will use the Bouncy Castle library to Encode/Decode binary data in Base64 format. Although this library has a lot more and can be used for Encryption/Decryption using different methods, today we will focus on the Encoding/Decoding part. You can download the library using the following link:


Make sure you download the J2ME version. When you download this version, you'll get the full source code and when you build it, you get a 1.7MB library... which may be too much for a mobile application. Anyway, with the source code you can make a build of some classes and not of all of them. That's how I made a build with only the encoders packages and classes and is just about 14KB. If you need it, just ask for it you can download it using this link.

To work with Base64 in JavaMe (J2ME) the bouncy castle library comes with the org.bouncycastle.util.encoders.Base64 class. Next is how you can use it in your MIDlet:


import org.bouncycastle.util.encoders.Base64;

...
    /**
     * As an example, encodes a String and then it decodes it.
     * Prints to console the result of the encode/decode.
     */
    public void encodeDecodeExample() {
      
        String word = "New word to encode using Base64 also special "
                + "chars like Ñ and ó";
      
        System.out.println("Encoding: ");
        System.out.println(word);
      
        byte[] coded = Base64.encode(word.getBytes());
        String strCoded = new String(coded);
      
        //prints the encoded word
        System.out.println("Result: ");
        System.out.println(strCoded);

        System.out.println("Decoding: ");
        System.out.println(strCoded);
      
        byte[] decoded = Base64.decode(strCoded);
        String strDecoded = new String(decoded);

        //prints the decoded word
        System.out.println("Result: ");
        System.out.println(strDecoded);
    }
...

When the previous code is run, the console output shows the following:




Encoding: 
New word to encode using Base64 also special chars like Ñ and ó

Result: 
TmV3IHdvcmQgdG8gZW5jb2RlIHVzaW5nIEJhc2U2NCBhbHNvIHNwZWNpYWwgY2hhcnMgbGlrZSDRIGFuZCDz

Decoding: 
TmV3IHdvcmQgdG8gZW5jb2RlIHVzaW5nIEJhc2U2NCBhbHNvIHNwZWNpYWwgY2hhcnMgbGlrZSDRIGFuZCDz

Result: 
New word to encode using Base64 also special chars like Ñ and ó


You can see that first it prints the String varible called 'word' encoded, after that it decodes the encoded word and prints the decoded word. You only need to have the byte array of data and invoke the proper methods.

This library is pretty simple to use and it gives you the power of transmit binary data as text. As we mentioned before, if you need to send, for example, an Image as text (JSON String? XML?).

This is a small part of the bouncy castle library. It has many encryption/decryption method to be used in your mobile applications. In future posts we will explore some of these methods to add security to our mobile applications.

This concludes our introduction to Base64 in JavaMe (J2ME), wait for the next post where we will use it to send images from the server to the mobile client using, once again, JSON Strings.

see ya soon!


References:

Base64. 2010. Wikipedia [online].
Available on Internet: http://en.wikipedia.org/wiki/Base64
[accessed on December 28 2010].

The Legion of the Bouncy Castle. bouncycastle.org [online].
Available on Internet: http://www.bouncycastle.org/
[accessed on December 28 2010].

Saturday, December 25, 2010

Servlet-GSON vs JSONME-JavaME II

This is the third and last part of our compression saga in Java Me (J2ME). Right until now, we have seen two libraries: TinyLine Utils (Decompress only) and JAZZLIB (Compress/Decompress). You can use whichever you want based on the requirements of your project.

In this post, we are going to improve our Servlet-GSON vs JSONME-JavaME example. We are adding compression support to the server side and decompression support on mobile client side. We will calculate the size of the data to be sent over the network before and after compression. This is helpful, when you don't have unlimited data connection on the mobile client and you need to reduce costs.

So, let's begin with the server side, the following code is the compression method used in our servlets project:

import java.util.zip.GZIPOutputStream;
...
    /**
     * Compress the String parameter
     * @param message
     * @return byte array of compressed data
     * @throws IOException if any error
     */
    public static byte[] compress(String message) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gz = new GZIPOutputStream(out);

        gz.write(message.getBytes());
        gz.flush();
        gz.close();

        out.flush();
        byte[] array = out.toByteArray();
        out.close();

        return array;

    }
...


As we are on server side, we can use the java.util.zip.GZIPOutputStream class in order to compress the data. Next, is the servlet code we have seen before but now it uses the compress method:

...
    /**
     * Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //sets the type of response and the charset used
        //Be careful, on your mobile client, read the response using the same charset
        response.setContentType("application/json; charset=UTF-8");

        //create some clients and add them to the vector
        Vector vClients = new Vector();

        Client clientOne = new Client();
        clientOne.setName("Alexis");
  //Note de special characters to be transmitted
        clientOne.setLastName("López Ñ");
        clientOne.setId("123456");

        vClients.add(clientOne);

        Client clientTwo = new Client();
        clientTwo.setName("Second");
        clientTwo.setLastName("Client");
        clientTwo.setId("987534");

        vClients.add(clientTwo);

        Client clientThree = new Client();
        clientThree.setName("Colombia");
        clientThree.setLastName("JUG");
        clientThree.setId("555555");

        vClients.add(clientThree);

        //convert the clients vector to JSON using GSON, very Easy
        Gson gson = new Gson();
        String jsonOutput = gson.toJson(vClients);

        System.out.println("*****JSON STRING TO RESPONSE*****");
        System.out.println(jsonOutput);
        System.out.println("*********************************");

  System.out.println("Length of String in bytes = " 
                                    + jsonOutput.getBytes().length);
        //compress de data
        byte[] compressed = compress(jsonOutput);
        System.out.println("Length of compressed bytes = " 
                                    + compressed.length);

        //send the binary response
        ServletOutputStream out = response.getOutputStream();
        out.write(compressed, 0, compressed.length);
        out.flush();
        out.close();
    }
...


As you can see, is the same method used in the first version of our Servlet-GSON vs JSONME-JavaME example, but now it uses the compress method before writing the response. Another difference, is that this time we are writing binary data so we use the javax.servlet.ServletOutputStream. As we are writing binary data, there is no need to be careful with the charset when reading the response on the mobile client side. Following is the output from the previous code:


INFO: *****JSON STRING TO RESPONSE*****
INFO: [{"name":"Alexis","lastName":"López Ñ","id":"123456"},{"name":"Second","lastName":"Client","id":"987534"},{"name":"Colombia","lastName":"JUG","id":"555555"}]
INFO: *********************************

INFO: Length of String in bytes = 157
INFO: Length of compressed bytes = 114


From the previous output, you can see that there is some compression. To be more accurate you can use a sniffer or protocol analizer (such as wireshark) in order to detect the packages sent over the network and to get the exact amount of bytes sent from the server to the mobile client.

Now let's check the decompression method on the mobile client side:

import com.tinyline.util.GZIPInputStream;
//You can also use net.sf.jazzlib.GZIPInputStream;
...
   /**
     * Decompress the data received in the Stream.
     * @param in <code>InputStream</code> of the connection where the
     * compressed data is received.
     * @return <code>String</code> representing the data decompressed
     * @throws IOException if there is any error during reading
     */
    public static String deCompress(InputStream in) throws IOException {
        StringBuffer sb = new StringBuffer();

        GZIPInputStream gz = null;
        try {
            gz = new GZIPInputStream(in);
            int c = 0;
            while ((c = gz.read()) != -1) {
                sb.append((char) c);
            }
        } finally {
            if (gz != null) {
                gz.close();
            }
        }

        return sb.toString();
    }
...

As we are only decompressing data, you can use any of the two libraries we have evaluated before. Next is the method that connects to the servlet, opens the InputStream and maps JSON Strings to Objects:

...
   /**
     * Connects to the server in order to obtain information of the clients
     * @return Vector of <code>Client</code> objects
     * @throws IOException if errors with the connections
     */
    public Vector getClients() throws IOException {
        //This is my servlet’s URL
        String URL = "http://localhost:8080/GSON_Servlet/ClientsServlet";
        Vector vClients = new Vector();

        HttpConnection http = null;
        InputStream in = null;
        try {
            //Open HttpConnection using POST
            http = (HttpConnection) Connector.open(URL);
            http.setRequestMethod(HttpConnection.POST);
            //Content-Type is must to pass parameters in POST Request
            http.setRequestProperty("Content-Type",
                                                 "application/x-www-form-urlencoded");

            //decompress the data
            in = http.openInputStream();


            //IMPORTANT: As the servlet wrote the data using a binary stream,
            //there is no need to use a special charset
            String jSonString = deCompress(in);

            System.out.println("*****JSON STRING RECEIVED*****");
            System.out.println(jSonString);
            System.out.println("******************************");

            //Parse the JSON String to obtain a Vector of clients
            vClients = Client.fromJSONs(jSonString);

        } //whatever happens, close the streams and connections
        finally {
            if (in != null) {
                in.close();
            }

            if (http != null) {
                http.close();
            }
        }

        return vClients;
    }
...

As said, the previous code connects to the server, reads the response, decompress the response and maps JSON Strings to Objects. The output is shown next, check that the special characters are ok even if we didn't used a special charset when reading the response from the server:


*****JSON STRING RECEIVED*****
[{"name":"Alexis","lastName":"López Ñ","id":"123456"},{"name":"Second","lastName":"Client","id":"987534"},{"name":"Colombia","lastName":"JUG","id":"555555"}]
******************************
*****CLIENT INFORMATION*****
Client 1 name = Alexis
Client 1 last name = López Ñ
Client 1 ID = 123456

Client 2 name = Second
Client 2 last name = Client
Client 2 ID = 987534

Client 3 name = Colombia
Client 3 last name = JUG
Client 3 ID = 555555


Ok, that's it for this post. I hope I was clear enough, if you have any comments or questions do not hesitate to contact me or to leave a comment.

see ya soon!


References:

A pure java implementation of java.util.zip library. 2010. SourceForge [online].
Available on Internet: http://jazzlib.sourceforge.net/
[accessed on December 20 2010].

Jazzlib Java Me. November 2010. STAFF [online].
Available on Internet: http://code.google.com/p/staff/downloads/list?q=jazzlib
[accessed on December 20 2010].

TinyLine Utils. 2010. TinyLine [online].
Available on Internet: http://www.tinyline.com/utils/index.html
[accessed on December 11 2010].

Using JavaScript Object Notation (JSON) in Java ME for Data Interchange. Agosto 2008. Oracle [online].
Available on Internet: http://java.sun.com/developer/technicalArticles/javame/json-me/
[accessed on October the 26th 2010].

meapplicationdevelopers: Subversion. 2007. Oracle [online].
Available on Internet: https://meapplicationdevelopers.dev.java.net/source/browse/meapplicationdevelopers/demobox/mobileajax/lib/json/
[accessed on October the 27th 2010].

Monday, December 20, 2010

Compression in JavaMe II

This is the second part of our compression saga in Java Me. In the previous post, we used the TinyLine GZIPInputStream to decompress data from an InputStream. It is useful when your application receives more information than it sends. But, what happens if you still need to compress data on the mobile client side before sending it to the server?

There is a library called JAZZLIB which is an implementation of the JSE java.util.zip package that uses pure Java, no native code. You can get more information about this JSE library in the following link:


The last link takes you to the JSE JAZZLIB library, for a Java Me implementation that comes with the GZIPInputStream and the GZIPOutputStream streams to decompress or compress data, follow this link:


Once you have imported tha library in your project, you can start compressing or decompressing directly on the mobile client side. The example of this post will focus only on two methods: One for the compression and the other for the decompression.

The following code represents the decompression method, you can realize that is the same decompression method used in the previous post, the only change is that this time we import the net.sf.jazzlib.GZIPInputStream class instead of the com.tinyline.util.GZIPInputStream class, but the method's java code doesn't change.

import net.sf.jazzlib.GZIPInputStream;
...
    /**
     * Decompress the data received in the Stream.
     * The stream is not closed in this method.
     * @param in <code>InputStream</code> of the connection where the
     * compressed data is received.
     * @return <code>String</code> representing the data decompressed
     * @throws IOException if there is any error during reading
     */
    public static String deCompress(InputStream in) throws IOException {
        StringBuffer sb = new StringBuffer();

        GZIPInputStream gz = new GZIPInputStream(in);
        int c = 0;
        while ((c = gz.read()) != -1) {
            sb.append((char) c);
        }

        gz.close();

        return sb.toString();
    }
...

The InputStream parameter could be one of:

  • +HttpConnection.openInputStream():InputStream
  • +SocketConnection.openInputStream():InputStream
  • +InputConnection.openInputStream():InputStream

Another approach to decompress data is to receive a compressed array of bytes instead of a stream as the following:

import net.sf.jazzlib.GZIPInputStream;
...
   /**
     * Decompress the data received in the array of bytes.
     * @param bytes data array compressed
     * @return <code>String</code> representing the data decompressed
     * @throws IOException if there is any error during reading
     */
    public static String deCompress(byte[] bytes) throws IOException {
        StringBuffer sb = new StringBuffer();

        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        GZIPInputStream gz = new GZIPInputStream(in);
        int c = 0;
        while ((c = gz.read()) != -1) {
            sb.append((char) c);
        }

        gz.close();

        return sb.toString();
    }
...

Notice that the net.sf.jazzlib.GZIPInputStream decompress on the fly, you don't have to invoke any other method than +read():int.

Now we want to compress data using the net.sf.jazzlib.GZIPOutputStream class. It is not difficult either check the following code:

import net.sf.jazzlib.GZIPOutputStream;
...
   /**
     * Compress the data received as a String message.
     * @param message <code>String</code> to be compressed
     * @return <code>String</code> representing the data compressed
     * @throws IOException if there is any error during writing
     */
    public static String compress(String message) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gz = new GZIPOutputStream(out);
        byte[] bytes = message.getBytes();
        gz.write(bytes, 0, bytes.length);

        //IMPORTANT, acording to the JAZZLIB API documentation,
        //the +close():void method:
        //Writes remaining compressed output data to the output stream and closes it.
        //so you BETTER invoke it, before returning the compressed array data
        gz.close();

        byte[] array = out.toByteArray();
        out.close();

        return new String(array);
    }
...

For compression we use the net.sf.jazzlib.GZIPOutputStream class. It does compression on the fly, you just have to invoke the +write(byte[],int,int):void method in order to get the data compressed. Notice that you have to invoke the +close():void method before you can get the data compressed or you will have an incomplete compressed data. Another approach to the previous code is the following:

import net.sf.jazzlib.GZIPOutputStream;
...
   /**
     * Compress the data received as a String message.
     * @param message <code>String</code> to be compressed
     * @param out OutputStream to write compressed data to.
     *  It is not closed in this method.
     * @throws IOException if there is any error during writing
     */
    public static void compress(String message, OutputStream out)
    throws IOException {
        GZIPOutputStream gz = new GZIPOutputStream(out);
        byte[] bytes = message.getBytes();
        gz.write(bytes, 0, bytes.length);

        //IMPORTANT, acording to the JAZZLIB API documentation,
        //the +close():void method:
        //Writes remaining compressed output data to the output stream and closes it.
        //so you BETTER invoke it, before returning the compressed array data
        gz.close();
    }
...

In this case, we receive the message to be compressed and the stream where the compressed data is written to. The stream can be one of:

  • +HttpConnection.openOutputStream():OutputStream
  • +SocketConnection.openOutputStream():OutputStream
  • +OutputConnection.openOutputStream():OutputStream.

Ok, so that's it for this post. Now we have seen two libraries to decompress data on the mobile client and one of them can be used to compress data as well. In the next post, we will integrate compression of data to the Servlet-GSON vs JSONMe-JavaMe example to optimize the amount of data sent over the network.

see you soon!


References:

A pure java implementation of java.util.zip library. 2010. SourceForge [online].
Available on Internet: http://jazzlib.sourceforge.net/
[accessed on December 20 2010].

Jazzlib Java Me. November 2010. STAFF [online].
Available on Internet: http://code.google.com/p/staff/downloads/list?q=jazzlib
[accessed on December 20 2010].