Saturday, August 3, 2013

Simplify, Ruthlessly!

While the current lightness and versatility of the j2me app is satisfactory, there still seems to be one Achilles's heel that spoils the deal for the rest. User registration has the greatest friction, and considering its position in the use-hierarchy of the app, has the potential for massive drop-off.

Best possible solution for tackling this problem has been detailed by Chinedu here http://sayaj2me.blogspot.com/2013/07/real-world-testing.html?showComment=1374598239927#c8073988122514701011

This leaves the problem of how to send international SMSes which usually attract ridiculous charges, especially in the developing world(politically correct term :) . So far in my research, the solution lies in creating a much more complex registration infrastructure but I believe the dividends will pay off.

This involves the 'buying' of local numbers and hosting them on a central internet-available server. Therefore, the app will only send to that user's local number hence charging him a local rate for text(acceptable). The hosted number calls the Saya server with requisite parameters and the registration cycle kicks off.

Currently, number hosting will cost around $100 per number per month. We can start by leveraging on the countries with the most potential users and build out from there.

Tuesday, July 23, 2013

Real World Testing


Saya is greatly liked by real world people who have sampled it. Once logged in and inside the app, everything is easy and straight forward to use. However, there are some pain points that pose a real threat to loosing out on users:
  1. Registration process – This is by far the most painful part of the app. It's tedious and some users do not understand why it's necessary(coupled with the instinctive feeling of avoiding to give out their phone numbers aimlessly). Most users are confused about the applicability of the received SMS and what to do with it(some I observed reply to that SMS with the code :) . All in all, the general verdict was that a way should be found that takes away registration from the app. *Possible solution illuminated below
  2. Notifications – On new apps there are 2 notifications. On the 2nd one “You currently have no friends. Please Invite or Sync.”, most users gloss over this and choose their required action(*Reading phonebook past 215 contacts seems to be a problem—troubleshooting still continuing). The first notification “Saya may collect device info to enhance experience.” causes relatively more confused than the former. Users have been observed to select the “Read more” option more than “Dismiss”. Good news however is that all this options have a good 'dead end' and the user cannot possibly stray out of the flow.

*Possible Solution to the 1st problem
We can provide an online portal where the user handles the authentication there. I.E
a) Ask for user number
b) Send user code SMS
c) Ask user for the code to authenticate it's his number
d) Send link of prepackaged app to his number

Concentrating on the last option(d), the app can be prepackaged in a number of ways
  1. Recompile on the fly with the number hardwired in the app. This unfortunately is not possible for a signed app, otherwise you would have to sign the app every time it's recompiled.
  2. Add the user number as a field in the JAD file.

Wednesday, July 17, 2013

The History Of Ctrl + Alt + Delete


code1

In the spring of 1981, David Bradley was part of a select team working from a nondescript office building in Boca Raton, Fla. His task: to help build IBM’s new personal computer. Because Apple and RadioShack were already selling small stand-alone computers, the project (code name: Acorn) was a rush job. Instead of the typical three- to five-year turnaround, Acorn had to be completed in a single year.

One of the programmers’ pet peeves was that whenever the computer encountered a coding glitch, they had to manually restart the entire system. Turning the machine back on automatically initiated a series of memory tests, which stole valuable time. “Some days, you’d be rebooting every five minutes as you searched for the problem,” Bradley says. The tedious tests made the coders want to pull their hair out.

So Bradley created a keyboard shortcut that triggered a system reset without the memory tests. He never dreamed that the simple fix would make him a programming hero, someone who’d someday be hounded to autograph keyboards at conferences. And he didn’t foresee the command becoming such an integral part of the user experience.

Bradley joined IBM as a programmer in 1975. By 1978, he was working on the Datamaster, the company’s early, flawed attempt at a PC. It was an exciting time—computers were starting to become more accessible, and Bradley had a chance to help popularize them.

In September 1980, he became the 12th of 12 engineers picked to work on Acorn. The close-knit team was whisked away from IBM’s New York headquarters. “We had very little interference,” Bradley says. “We got to do the design essentially starting with a blank sheet of paper.”

Bradley worked on everything from writing input/output programs to troubleshooting wire-wrap boards. Five months into the project, he created ctrl+alt+del. The task was just another item to tick off his to-do list. “It was five minutes, 10 minutes of activity, and then I moved on to the next of the 100 things that needed to get done,” he says. Bradley chose the keys by location—with the del key across the keyboard from the other two, it seemed unlikely that all three would be accidentally pressed at the same time. Bradley never intended to make the shortcut available to customers, nor did he expect it to enter the pop lexicon. It was meant for him and his fellow coders, for whom every second counted.

The team managed to finish Acorn on schedule. In the fall of 1981, the IBM PC hit shelves—a homely gray box beneath a monitor that spit out green lines of type. Marketing experts predicted that the company would sell a modest 241,683 units in the first five years; company execs thought that estimate was too optimistic. They were all wrong. IBM PC sales would reach into the millions, with people of all ages using the machines to play games, edit documents, and crunch numbers. Computing would never be the same.
code2

And yet, few of these consumers were aware of Bradley’s shortcut quietly lingering in their machines. It wasn’t until the early 1990s, when Microsoft’s Windows took off, that the shortcut came to prominence. As PCs all over the country crashed and the infamous “blue screen of death” plagued Windows users, a quick fix spread from friend to friend: ctrl+alt+del. Suddenly, Bradley’s little code was a big deal. Journalists hailed “the three-finger salute” as a saving grace for PC owners—a population that kept growing.

In 2001, hundreds of people packed into the San Jose Tech Museum of Innovation to commemorate the 20th anniversary of the IBM PC. In two decades, the company had moved more than 500 million PCs worldwide. After dinner, industry luminaries, including Microsoft chairman Bill Gates, sat down for a panel discussion. But the first question didn’t go to Gates; it went to David Bradley. The programmer, who has always been surprised by how popular those five minutes spent creating ctrl+alt+del made him, was quick to deflect the glory.

“I have to share the credit,” Bradley joked. “I may have invented it, but I think Bill made it famous.”

Tuesday, July 16, 2013

Another Pesky issue

Been working on monitoring 'Connection Lost' and having opted to go with 'Heartbeat' method( http://stackoverflow.com/a/486695/1048839 ), I stumbled upon a pesky problem that was stubborn enough to have Mawuli convince me to reboot my laptop(1st time in 2 months). Even then, the problem persisted.

To handle new app updates, the app listens for updated from the server and acts accordingly.



  • public void handleNotify(JSONObject json) throws JSONException{      
            JSONObject json_obj = json.getJSONObject("result");        //Check for update variable        if(json_obj.getString("type").equals("update")){System.out.println("Update Found for..."+app_version);            String[] latest_version = split(json_obj.getString("ver")".");            String[] current_version = split(app_version, ".");          
                //Assign new verion number to app_version variable to avoid future            //checks during current session            app_version = json_obj.getString("ver");            //Compare versions and enable update download            if(!latest_version[0].equals(current_version[0])){                //Force download update                cmd_update = new Command("Update", Command.OK0);                cmd_exit = new Command("Exit", Command.EXIT1);                Alert alert = new Alert("Update""New Update available.\nPlease Download"null, AlertType.CONFIRMATION);                alert.addCommand(cmd_update);                alert.addCommand(cmd_exit);                alert.setCommandListener(this);             
                    Display.getDisplay(this).setCurrent(alert);             
                    //Sleep midlet                try {                  
                        Thread.sleep(100000);                    //Force App close                    instance.notifyDestroyed();                } catch (InterruptedException ex) {                    ex.printStackTrace();                }            }else if(!latest_version[1].equals(current_version[1])){                //Ask to update                cmd_update = new Command("Update", Command.OK0);              

                    Alert alert = new Alert("Update""New Update available.\nPlease Download"null, AlertType.CONFIRMATION);                alert.addCommand(cmd_update);              
                    alert.setCommandListener(this);             
                    Display.getDisplay(this).setCurrent(alert);                             
                    //Sleep midlet                try {                    for(int i=10; i>0; i--){                      
                            alert.setString("["+Integer.toString(i) +"] "+"New Update available.\nPlease Download");                        Thread.sleep(1000);                    } 
                     
                    } catch (InterruptedException ex) {                    ex.printStackTrace();                }            }else if(!latest_version[2].equals(current_version[2])){                //Ask to update                cmd_update = new Command("Update", Command.OK0);              

                    Alert alert = new Alert("Update""New Update available.\nPlease Download"null, AlertType.CONFIRMATION);              
                    alert.addCommand(cmd_update);             
                    alert.setCommandListener(this);             
                    Display.getDisplay(this).setCurrent(alert);             
                    //Sleep midlet                try {                    for(int i=10; i>0; i--){                      
                            alert.setString("["+Integer.toString(i) +"] "+"New Update available.\nPlease Download");                        Thread.sleep(1000);                    }                                    
                    } catch (InterruptedException ex) {                    ex.printStackTrace();                }            }          
             
            }     
         
        }//--End of handleNotify(JSONObject);
  • The problems occurs in the small sleeping thread for the last 2 options as it counts from 10 down to one. On the 3rd count(i.e no.7), it fires the cmd_update by itself and therefore tries to execute an update. The solution, weirdly enough, I found was to add another command to the alert and that stopped cmd_update from firing.

    //Ask to update
                    cmd_update = new Command("Update", Command.OK, 0);
                    cmd_exit = new Command("Exit", Command.EXIT, 1);
                    Alert alert = new Alert("Update", "New Update available.\nPlease Download", null, AlertType.CONFIRMATION);              
                    alert.addCommand(cmd_update);
                    alert.addCommand(cmd_exit);
                   
                    alert.setCommandListener(this);
                   
                    Display.getDisplay(this).setCurrent(alert);

    Wednesday, July 10, 2013

    Might have lost the battle but the war is still on

    36 hours later and I still can't figure out what causes "Unhandled Exception" just after handling notify for app update(handleNotify()).

    So I decided to yield(for now) and re-write my code around the problem. Effectively, updates will only be handled for a registered app. They will not he handled for the first session of the app where registration is carried. They will only be handled for subsequent app sessions thereafter. See attached code.


    //Check for updates
            try {
                JSONObject json = new JSONObject(aPacket.getUTF8());
                if(json.getString("mod").equals("notify")){
                    try {
                        //Check for bearer to determine if app is logged in
                        if(packet_handler.getBearer() != null){
                            handleNotify(json);
                        }
                    } catch (RecordStoreException ex) {
                        ex.printStackTrace();
                    }
                }
            } catch (JSONException ex) {
                ex.printStackTrace();
            }

    P/S: Update is handled according to Chinedu's prescribed handling techniques i.e for version x.y.z
    where..
    x denotes major release hence force an update(no grace. App timeout&exit after 100 secs)
    y denotes a feature release hence ask user to update(10 sec grace applied on screen)
    z denotes a bug fix hence ask user to update(10 sec grace applied on screen)

    Tuesday, July 9, 2013

    Handling dead URLs

    Came across this problem when handling an improbable dead url; http://api.saya.im/locbyip which is the Location By IP API. Because I always assumed it will give back something, I failed to handle the odd case of the url being dead.

    This can be done with a small one-liner addition
    throw new IOException("Response:"+http.getResponseCode()+", "+http.getResponseMessage());

    Here's the full code of the openUrl method


    public String openUrl(String url) throws IOException, SecurityException{
            byte[] data = null;      
                   
            HttpConnection http = (HttpConnection)Connector.open(url, Connector.READ_WRITE, true);
           
            //set custom headers
            http.setRequestProperty("User-Agent", "Profile/MIDP-2.0 Configuration/CLDC-1.1");
            http.setRequestProperty("Accept_Language", "en-US");
                           
            //Read response
            if(http.getResponseCode() == HttpConnection.HTTP_OK){
                int len = (int)http.getLength();
                is = http.openInputStream();
                if(is == null){
                    throw new IOException("Cannot open HTTP InputStream, Aborting..");
                }
                //Else continue reading stream
                response = "";
                Reader in = new InputStreamReader(is);
                StringBuffer temp = new StringBuffer(1024);
                char[] buffer = new char[1024];
                int read;
                while((read = in.read(buffer, 0, buffer.length)) != -1){
                    temp.append(buffer, 0, read);
                }
                response = temp.toString();
            }else{
                System.out.println("Response:"+http.getResponseCode()+", "+http.getResponseMessage());
                response = null;
               
                throw new IOException("Response:"+http.getResponseCode()+", "+http.getResponseMessage());          
            }
            if(http != null){
                http.close();
            }
            if(is != null){
                is.close();
            }
           
            return response;
           
        }//--End of openUrl()

    Monday, June 17, 2013

    Private Chat packets(reversal)

    While it makes sense, it's worth noting that when A sends a private chat to B, the 'from' variable = A and 'room' variable = B. This also happens vice-versa. The issue here is that since ALL messages are broadcasted across the users, then 'room' value keeps changing. Sample this

    1. Broadcast message from me

    processPacket...{"ref":"chat","mod":"chat","status":200,"err":null,"result":{"type":"p","ctype":"t","id":"2E3QW1AW34ay7y410wfQ","msg":"Hey, please send another message","from":"254712249559","room":"233200238442","coord":null,"ts":"1371461833"}}
    Reading data...

    2. Broadcast message from friend with reply
    processPacket...{"ref":"3_faaa586c9aa38f01968715d762e3c995","mod":"chat","status":200,"err":null,"result":{"type":"p","ctype":"t","id":"2E3QWagtWQM0RYWluxOK","msg":"another message","from":"233200238442","room":"254712249559","coord":"5.645256:-0.151525","ts":"1371461857"}}
    Reading data...

    As seen, the 'room' variable keeps changing. To handle this change:
    1. Check if chat is private
    2. Check that chat is not from me
    3. Apply room reversal


    /*
                 * Handle reversal for private chat--arghh!!!!
                 *
                 */
                if(json.getJSONObject("result").getString("type").equals("p")){
                    //Check if message is not from me and reverse roomid
                    if(!json.getJSONObject("result").getString("from").equals(phone)){
                        roomid = json.getJSONObject("result").getString("from");
                    }
                }
                

    java.io.IOException: error 32 during TCP write


    On netbeans project.Right click on project->properties->manage emulators->tools & extensions-> and changing the Http Version from HTTP1.1 to HTTP1.0. This works in Sun Wireless Toolkit 2.5.2.


    Saturday, June 15, 2013

    Cleaning up final app

    Been cleaning, tweaking and generally finalizing the app for submition. One think pointed out by Mawuli is the fact that non-English speakers will find it hard to use the app and therefore am customizing to accommodate them.

    One of the changes revolves around the registration process where the (English)language is being simplified and also the process made more fool proof by providing a means for the user to choose which country they are in. This brings onboard a some complexities,namely file size increase and code refactoring.

    I've attached a CSV file(countrycodes.txt) as a resource in the images folder which contains international dialing codes for all countries. The file has increased the overall app size by 3.5kb.

    Since users are not mandated to append their international dialing codes to their numbers, I have to curate the number and country chosen so as to come up with a valid mobile number. Below is the guidance am employing in this task.


    All mobile numbers need to be entered in this format.
    Coutry Code, Network prefix (no leading 0), Mobile Number.
    For example, the mobile number 072 244 3259 in South Africa would be formatted like this:


    //www.digitalsms.net/faq/numberformat/index.php

    Monday, May 20, 2013

    New Changes

    Great thanks @Chinedu and @Badu for porting the Android ws for me to j2me.

    Just finished rebuilding new the app again. Structure is more or less the same except with some changes in the login process. Login now handled by a new class, loginClass. Reused RMS read and write procedures. Great design where everything stored in the RMS is based on a hashlist, hence enabling quick access to values. RMS methods are part of the sayaConnection class.

    More to come...

    Wednesday, April 10, 2013

    Happy as a bald Baby in Red Overalls

    Firstly, great thanks and props to Chinedu for shedding the light on this very dark problem, but I finally manged to fix it!

    My small rant
    "The guys that did JWebSocket went out of their way to port the whole implementation to j2me(even though they made some errors with reading the byte 0xff)." here had me stomped when I had to redo the codebase to work exclusively with websockets. The problem was figuring where the start(0x00) and end(0xff) of the json websocket packets were. And since j2me was finding it almost impossible to read these two values, Chinedu found out something based on this documentation here

    5.6. Data Frames

    Data frames (e.g., non-control frames) are identified by opcodes where the most significant bit of the opcode is 0. Currently defined opcodes for data frames include 0x1 (Text), 0x2 (Binary). Opcodes 0x3-0x7 are reserved for further non-control frames yet to be defined. Data frames carry application-layer and/or extension-layer data. The opcode determines the interpretation of the data: Text The "Payload data" is text data encoded as UTF-8. Note that a particular text frame might include a partial UTF-8 sequence; however, the whole message MUST contain valid UTF-8. Invalid UTF-8 in reassembled messages is handled as described in Section 8.1. Binary The "Payload data" is arbitrary binary data whose interpretation is solely up to the application layer.

    5.7. Examples

    o A single-frame unmasked text message * 0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f (contains "Hello") o A single-frame masked text message * 0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58 (contains "Hello") o A fragmented unmasked text message * 0x01 0x03 0x48 0x65 0x6c (contains "Hel") * 0x80 0x02 0x6c 0x6f (contains "lo")


    To break it down, this is what it entails.

    1. Read the incoming data stream like this b = is.read()
    2. Check for this value '129' which signifies the start of the package. "129 is 0x81 which is the marker for a single-frame unmasked text message"
    3. Read the next byte which represents the length of the packet.
    4. Iterate as many times as the length acquired to read the whole packet

    Here's my method:
    public StringBuffer readStream(InputStream is){
                     StringBuffer sb = null;              
                    int length = -1;
                     try {
                        int b = is.read();                
                        if (b != -1) {
                            if (b == 129) {//Set length
                                length = is.read();                        
                            }
                            sb = new StringBuffer();
                         
                                for (int i = 0; i < length;) {
                                    int c=is.read();                              
                                    sb.append((char) c);
                                    i++;    //Increment after /*do*/
                                }                      
                         
                        }

                    } catch (IOException ex) {
                        isRunning = false;
                        ex.printStackTrace();
                    }
                   
                     return sb;
                 }
    UPDATE: This actually failed. Server underwent a major rebuild with websockets as the ONLY communication channel.

    Wednesday, April 3, 2013

    Error during Obfuscation

    Trying to Obfuscate the saya code base, kept bumping into this error

    Warning: there were 1 instances of library classes depending on program classes.
             You must avoid such dependencies, since the program classes will
             be processed, while the library classes will remain unchanged.
    Error: Please correct the above warnings first.
    The issue it seems was caused by these two classes
    Warning: library class com.sun.ukit.jaxp.Parser extends or implements program class org.xml.sax.Locator 
    The solution was take apart the whole code and piece it together, class by class(yes-tedious I know). But finally, it worked.!

    Thanks to below link for guidance :)


    http://proguard.sourceforge.net/manual/troubleshooting.html#dependency

    Monday, March 25, 2013

    Crashes!

    Am refactoring code on Netbeans. So far in less than 30 mins, 3 Java crashes with the latest one causing a kernel panic!


    Tsk Tsk Tsk -- Monkeys!

    Edit--4th crash, System freeze!

    Sockets and/vs http

    By design, http should be pretty robust but since we're talking about Java here, that's another totally different story.

    The guys that did JWebSocket went out of their way to port the whole implementation to j2me(even though they made some errors with reading the byte 0xff). It's helpful that it maintains a relatively stable socket connection and will throw an Exception caught in the main app just incase a connection is lost or closed.

    Current problem while using http(and which has left me at an impasse) is Timeouts(covered here). This(while I might be terribly wrong), I attribute to the fact that every http connection is atomic and involves setting up and tearing a connection instance. Add to that the fact that different j2me CLDC* implementations restrict the number of connections(sometimes to as little as 2) and the darn s60 bug I found, using sockets over http starts to look a bit more interesting.

    As an implementation, Badu and the other guys server-side are working on it, but this is how  I would do it:

    • First of all, I'd like to piggy back on the current ws connection
    • To do this, I would have to shape my requests into a json format e.g {"type" : "POST", "content" : {/*JSON object/array of key=>value pairs to represent the parameters*/}}
    *CLDC-Connected Limited Device Configuration(where Limited really means LIMITED)

    Connection Timeouts

    Just noticed that the app has a number of connection Timeouts which caused the app to continue waiting on a response from a dead connection. This mean that the app would essentially wait forever for a connection that has already - less than ideal, needless to say.

    Researching, I found that I can define a boolean value when opening the connection which should essentially throw an Exception when the timeout reached with no response from server. 
    HttpConnection http = (HttpConnection)Connector.open(url, Connector.READ_WRITE, true);
    Again with the monkey design in Java, the parameter list does not take a time parameter hence, I cannot manually define the timeout myself. Instead, I have to wait for a system-dependent timeout that no-one seems to know the real value of(ranging from 60 secs to 5 mins-depending on who you ask). I tested this and did not get any Exception thrown even though the app lost connection for quite sometime(situ 15 mins).

    Bit the bullet on that and approached the problem from another angle. I create a timertask which after a period x will check to see if the connection has been read(using a boolean flag) and if not, try and close the connection. I then have it throw a custom Exception which I wanted to catch in the main app so that I can know that the connection timed out and handle accordingly.

    connectionTimeOut = true;
           
            final Timer timer = new Timer();
            TimerTask timeoutTask = new TimerTask(){
                public void run(){System.out.println("Timeout Expired:");
           
                    //Shutdown streams
                    try {
                        if(connectionTimeOut){
                            if(is != null){
                                //Close all streams
                               is.close();
                            }
                            timer.cancel(); //Shutdown timer
                            System.out.println("Timing out..");                      
                            //Throw exception to be handled in main class
                          kill = true;
                            throw new TimeoutException("Connection Timed Out!");
                        }
                    } catch (TimeoutException ex) {
                        ex.printStackTrace();
                    }  catch (IOException ex) {
                        ex.printStackTrace();
                    }
                }
            };
            timer.schedule(timeoutTask, 3000);
            

    Unfortunately, timerTask inherits from Thread which means that there's no way to catch the thrown Exception outside it's own thread(and hence the calling method). So in essence, am stuck with a half-baked thread which cleans up pending open connections after the timeout but which cannot communicate back to the original calling Thread(and hence method) to notify it of the action.

    P/S: Am still thinking some more on how to solve this problem. :(

    Friday, March 15, 2013

    s60 Bug

    I've been battling with this issue for the last couple of days. Very simply, this is what's up.
    When making a POST request in an s60 device, the first request goes through but subsequent requests immediately following fail. However, a requested submitted a few seconds later will work.

    After literally ripping the code apart, I found that a HTTP-400 error is what triggers the failure. Testing on another platform and language confirms that the server is not to blame here. More digging on the internet seems to point to a somewhat native issue with the underlying http methods and their hold on data/resources during and after a http request.(Even issues surrounding memory leak have been raised)

    http://www.developer.nokia.com/Community/Discussion/showthread.php?102878-http-error-400-with-Content-Type

    http://www.developer.nokia.com/Community/Discussion/showthread.php?67798-CHTTPFormEncoder-and-HTTP-CORE-10-Panic

    Tuesday, March 5, 2013

    Java and it's inherent stupidity(Textfield-a case study)

    That monkey called James Gosling needs to be shot off the tree that he's hanging!

    The monkies who designed java and it's implementation fucked again! Spent the whole night debugging Textfield which throws an incomprehensible Uncaught exception java/lang/IllegalArgumentException.
    This is because the value being passed is greater than the (compulsory)defined size of the Item.
    new TextField("Group Name", name, 10, TextField.ANY);
    Now, you might be tempted here to get the maxSize() for TextField, but the values am getting are stupid platform suggestions that make no sense(i.e 12). So because I know any device capable of installing Saya is also capable of holding at least 100 chars in a text field, I hardcoded the limit to such
    new TextField("Group Name", name, 100, TextField.ANY);
    But also was careful enough to make sure the dynamic content being inserted there(i.e name) does not exceed the limit.

    if(name.length()>99){
                        name = name.substring(0, 99);
                    }

    http://jcs.mobile-utopia.com/jcs/67140_TextField.html
    public class TextField extends Item
    TextField is an editable text component that may be placed into a {@link Form Form}. It can be given a piece of text that is used as the initial value.

    TextField has a maximum size, which is the maximum number of characters that can be stored in the object at any time (its capacity). This limit is enforced when the TextField instance is constructed, when the user is editing text within the TextField, as well as when the application program calls methods on the TextField that modify its contents. The maximum size is the maximum stored capacity and is unrelated to the number of characters that may be displayed at any given time. The number of characters displayed and their arrangement into rows and columns are determined by the device.

    The implementation may place a boundary on the maximum size, and the maximum size actually assigned may be smaller than the application had requested. The value actually assigned will be reflected in the value returned by {@link #getMaxSize() getMaxSize()}. A defensively-written application should compare this value to the maximum size requested and be prepared to handle cases where they differ.

    Saturday, March 2, 2013

    Working with Arrays

    So this is an example of the returned json for 'get_info' request
    {"success":"1","info":{"created":"2012-8-30 16:47:59","country":"Ghana","user_id":"2","onlinestatus":"1","last_login_datetime":"2013-3-2 22:13:10","nickname":"badu","discover":"0"},"interests":[" cook","eat"]}
    Interests is an array of values. The doc specifies updating interests at such
    interests - comma separated list of interests 
    Obviously, a suitable method for converting between arrays and a comma separated string value(s) is required. Unfortunately for Java and by extension J2ME, they never bothered to think that people might implementations similar to split and join in Python. So here goes..

    1. Split first. Very simple method for splitting a string into an array along defined separators

    public String[] split(String original, String separator)
                    {
                        Vector nodes=new Vector();
                        //Parse nodes into Vector
                        int index=original.indexOf(separator);
                        while(index>=0){
                            nodes.addElement(original.substring(0, index));
                            original=original.substring(index+separator.length());
                            index=original.indexOf(separator);
                        }
                        //Get the last node
                        nodes.addElement(original);
                        //Create split array
                        String[] result=new String[nodes.size()];
                        if(nodes.size()>0){
                            for(int loop=0; loop<nodes.size(); loop++)
                            {
                                result[loop]=(String)nodes.elementAt(loop);
                            }
                        }
                        return result;
                 }//--End of split()
         
    2. Now the slightly harder part. Convert the returned array in the json response to a string. First problem here is that getting the array involves returning it as a JSONArray which apparently is not castable to a normal String Array. So this is how you do it...
                //Convert JSONArray to String Array
                JSONArray key_array = json.getJSONArray("interests");
                String[] key_attributes = new String[key_array.length()];
                for(int i=0; i<key_array.length(); i++){
                    key_attributes[i] = key_array.getString(i);
                }
     This is followed by the join method which I implemented as such:

    public String join(String[] array, char separator){
            //Catch empty array
            if (array == null) return null;
            if(array.length == 0) return null;
           
            String joined_string = "";
            for(int i=0; i<array.length;){
                joined_string += array[i] + separator;
                i++;    //Don't forget to increment
            }
            //Truncate trailing seprator
            return joined_string.substring(0, joined_string.length()-1);
        }//--End of join()
    Now calling the method join, you MUST remember to provide the separator as a character. In java, it's not easy to type out a character variable in code and instead, one has to go around like this:
        String separator = ",";
                interests = join(key_attributes, separator.charAt(0));
    There you go folks, all done with Java's usual unfriendly long way of getting simple things done.!

    Wednesday, February 27, 2013

    ==, .equals(), compareTo(), and compare()

    This gave me hell..!


    Equality comparison: One way for primitives, Four ways for objects

    ComparisonPrimitivesObjects
    a == ba != bEqual valuesCompares references, not values. The use of == with object references is generally limited to the following:
    • Comparing to see if a reference is null.
    • Comparing two enum values. This works because there is only one object for each enum constant.
    • You want to know if two references are to the same object
    a.equals(b)N/ACompares values for equality. Because this method is defined in the Object class, from which all other classes are derived, it's automatically defined for every class. However, it doesn't perform an intelligent comparison for most classes unless the class overrides it. It has been defined in a meaningful way for most Java core classes. If it's not defined for a (user) class, it behaves the same as ==.
    It turns out that defining equals() isn't trivial; in fact it's moderately hard to get it right, especially in the case of subclasses. The best treatment of the issues is in Horstmann's Core Java Vol 1. [TODO: Add explanation and example]
    a.compareTo(b)N/AComparable interface. Compares values and returns an int which tells if the values compare less than, equal, or greater than. If your class objects have a natural order, implement theComparable<T> interface and define this method. All Java classes that have a natural ordering implement this (String, Double, BigInteger, ...).
    compare(a, b)N/AComparator interface. Compares values of two objects. This is implemented as part of theComparator<T> interface, and the typical use is to define one or more small utility classes that implement this, to pass to methods such as sort() or for use by sorting data structures such as TreeMap and TreeSet. You might want to create a Comparator object for the following.
    • Multiple comparisons. To provide several different ways to sort something. For example, you might want to sort a Person class by name, ID, age, height, ... You would define a Comparator for each of these to pass to the sort() method.
    • System class. To provide comparison methods for classes that you have no control over. For example, you could define a Comparator for Strings that compared them by length.
    • Strategy pattern. To implement a strategy pattern, which is a situation where you want to represent an algorithm as an object that you can pass as a parameter, save in a data structure, etc.
    If your class objects have one natural sorting order, you may not need this.

    Comparing Object references with the == and != Operators

    The two operators that can be used with object references are comparing for equality (==) and inequality (!=). These operators compare two values to see if they refer to the same object. Although this comparison is very fast, it is often not what you want.
    Usually you want to know if the objects have the same value, and not whether two objects are a reference to the same object. For example,
    if (name == "Mickey Mouse")   // Legal, but ALMOST SURELY WRONG
    
    This is true only if name is a reference to the same object that "Mickey Mouse" refers to. This will be false if the String in namewas read from input or computed (by putting strings together or taking the substring), even though name really does have exactly those characters in it.
    Many classes (eg, String) define the equals() method to compare the values of objects.

    Comparing Object values with the equals() Method

    Use the equals() method to compare object values. The equals() method returns a boolean value. The previous example can be fixed by writing:
    if (name.equals("Mickey Mouse"))  // Compares values, not references.
    
    Because the equals() method makes a == test first, it can be fairly fast when the objects are identical. It only compares the values if the two references are not identical.

    Other comparisons - Comparable<T> interface

    The equals method and == and != operators test for equality/inequality, but do not provide a way to test for relative values. Some classes (eg, String and other classes with a natural ordering) implement the Comparable<T> interface, which defines a compareTomethod. You will want to implement Comparable<T> in your class if you want to use it with Collections.sort() or Arrays.sort() methods.

    Defining a Comparator object

    As described in the table above on compare(), you can create Comparators to sort any arbitrary way for any class. For example, the String class defines the CASE_INSENSITIVE_ORDER comparator.

    If you override equals, you should also override hashCode()

    Overriding hashCode(). The hashCode() method of a class is used for hashing in library data structures such as HashSet andHashMap. If you override equals(), you should override hashCode() or your class will not work correctly in these (and some other) data structures.

    Shouldn't .equals and .compareTo produce same result?

    The general advice is that if a.equals(b) is true, then a.compareTo(b) == 0 should also be true. Curiously, BigDecimalviolates this. Look at the Java API documentation for an explanation of the difference. This seems wrong, although their implementation has some plausibility.

    Other comparison methods

    String has the specialized equalsIgnoreCase() and compareToIgnoreCase(). String also supplies the constantString.CASE_INSENSITIVE_ORDER Comparator.

    The === operator (Doesn't exist - yet?)

    Comparing objects is somewhat awkward, so a === operator has been proposed. One proposal is that
    a === b would be the same as ((a == b) || ((a != null) && a.equals(b)))

    Common Errors

    Using == instead of equals() with Objects
    When you want to compare objects, you need to know whether you should use == to see if they are the same object, orequals() to see if they may be a different object, but have the same value. This kind of error can be very hard to find.

    References

    Tuesday, February 26, 2013

    Sinister Trap-Json Object vs Json Array

    Badu made a change server side that allowed me to proceed with the default sync of contacts by uploading a  json array to the server. 4 hrs later and I was racking my brain why his server kept returning a 500(Internal Server Error) response until I found out that I was submitting a json object instead of array.

    What's the difference?
    Json object is demarcated by {} while json array is demarcated by [ ]. Yeah, it really was that trivial.

    Here's the bit of code that I used to change my object to array

    JSONArray json_array = new JSONArray();
    json_array.put(json);
    String params = "action=sync&phone="+phone+"&contacts="+json_array.toString();