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].