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