Saving User preferences on the server – cutting corners in Flex

It started as a simple task – I needed to store user preferences.  No problem SharedObject.getLocal() will do the job. Oh, by the way, we do not want to store the preferences on the local disk because users want to be able to switch workstations. SharedObject.getRemote()? No good. Who’s going to let you store the data on the server’s machine? And even if they will, how are you going to organize multi-user complex preferences?

Using DBMS is a no-brainer.  I’ll create a two-column table – userID and something with properties. What something? An object as a blob. What type of object? Hmm. How about ByteArray?  Rolling up the sleeves.

Before we proceed, let me remind you I spend many years doing  pure object-oriented programming in Java. You’ll see why it’s important in a little bit.

There is a lots of various user preferences to remember. For example, which is a default tab in TabNavigator, which columns should be visible in a datagrid and more.  Here’s what goues through my mind, “For simple preferences I’ll just use Strings and Numbers, and more complex ones I’ll put in a collection with key-value pairs. Then, I’ll create the object UserPrefs, add all the preferences to it, convert it into a ByteArray and will store it either in the SharedObject.data or send it over to a POJO for storage in the DBMS”.  Sounds good? Boy, I was wrong…

It started with the fact that I decided to use ActionScript 3 Dictionary class.  When I opened the language reference, I was surprised by several things:

a) The class Dictionary is inherited right from the Object and has only a couple of extra methods there. To iterate the dictionary, I was looking for a property length or something to know how many elements are there in my collection. Nope, there is not such thing there. Now it’s getting clearer how Flash Player’s VM is so light comparing to JVM.  No big deal. Will loop through the collection while it has some elements.

b) The fun began when I started adding my collections to the UserPrefs object trying  serialize them into the ByteArray for storing in the SharedObject.  When the user will start my application, I’ll de-serialize it using ByteArray.readObject(). Oopsie…Class deserialization is not supported in ActionScript 3. Need to recreate collections manually. But what if I’ll have collections inside collections?

Anyway, finally I got it to work…while debugging in Flash Player. The regular run-time kept giving me some errors…

My colleague  Anatole suggested, “Forget about Dictionary. In this case you can live with a simple Object, but use dynamic typing, and Flash will do deep copying”.  I followed his advice, the program became simpler, I got rid of my UserPref class and it works like a charm.  The following sample creates some hardcoded preferences for illustration purposes, converts them to byte array, saves them, and the on a button click retrieves and deserializes them. Conversion to byte array and back is done just to simplify communication with the Java back end, if any. Here’s the code:

<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute”>
 <mx:Button x=”173″ y=”16″ label=”Save Hardcoded Preferences” width=”185″ click=”saveData()”/>
 <mx:Button x=”203″ y=”46″ label=”Read Preferences”  click=”getSavedData()”/>
 <mx:TextArea x=”86″ y=”76″ width=”356″ id=”txtOutput” height=”156″/>
 <mx:Script>
  <![CDATA[
        private var prefs:SharedObject;
        private const COOKIE_SIZE:int=10000;
        private function getSavedData():void{

          prefs = SharedObject.getLocal(“UserPrefs5″);
                var myBA:ByteArray = prefs.data.justTheBytes;
             var recoveredUPrefs:Object=myBA.readObject();

                var defaultTab:String=recoveredUPrefs[“defaultTab”];
             txtOutput.text+= “default tab=”+defaultTab + “\n”;

                var visibleColumns:Object=recoveredUPrefs[“visibleColumns”];
                 for (var elem:String in visibleColumns){
                   txtOutput.text+= elem+”=”+visibleColumns[elem]+ “\n”;
                 }
           }
               private function saveData():void{
                prefs = SharedObject.getLocal(“UserPrefs5″);

                var uPrefs:Object= new Object();
                var visibleCol:Object = new Object();
    //which col’s to make visible
    visibleCol[“custName”]=”true”;
    visibleCol[“custAddress”]=”false”;
    visibleCol[“customerAge”]=”true”;
                uPrefs[“visibleColumns”]=visibleCol;
                // which tab in TabNavigator has to be opened first
                uPrefs[“defaultTab”]=”CanceledOrders”;
                // turning the object with prefs into a set of bytes
                var baPrefs:ByteArray = new ByteArray();
                baPrefs.writeObject(uPrefs);
                baPrefs.position = 0;

                // Saving the byte array on disk in a SharedObject
                prefs.data.justTheBytes=baPrefs;
                var flushStatus:String = null;   

                try {
   prefs.flush(COOKIE_SIZE);
                } catch (error:Error) {
                    trace(“Error…Could not write SharedObject to disk”+ error.message);
                }
           }     ]]>
 </mx:Script>
</mx:Application>

Press the button Save, and it’ll save the hardcoded preferences in a local shared object – a String called defaultTab and a collection of flags for visible columns.  Before saving, it serializes the Object (yes a simple Object with dynamically added properties) into an array of bytes and stores it in a local shared object.   If you want, you can pass this array to the backend for storage in the database.

Press the Read button, and the properties will be resurrected from the disk and added to the text area on the screen. Simple, elegant, and flexible.  Not exactly object-oriented. Sorry.

Yakov

4 thoughts on “Saving User preferences on the server – cutting corners in Flex

  1. You say “not exactly object-oriented”?

    Hehe…

    The first suggestion from Java camp will be not about OOP, but rather about using XML as preferences format :)
    Sure, shortly they suggest you to roll your own ActionScript JAXB atop of this 😉

    VS

  2. Valera,
    Interesting comment. I think Yakov’s point was to show the difference in thinking in scripting languages: (1-liners approach) versus strong-typing-compiler-knows-everything one. Syntax “compatibility” of ActionScript and Java cause people to use the same code and techniques. Two points, really:
    1 not to use classes as Object is quite flexible and should not be treated as sunctional compatibility.
    2. serialization / deserialization in Flex are way different from Java.

    XML deep copy serializer would make sense for some applications requiring both sides persistance and accessibility. However, I think for the most cases we would advocate rpc calls for simplicity

    Thank you
    Anatole

  3. Anatole,

    I believe that I understand Yakov’s point correctly. The only thing I’d like to emphasize is that Yakov grants too much to “open-mind thinking” of Java crowd (and I’m part of this crowd). EJB + XML descriptors, Spring + XML configurations, Hibernate + XML mappings… JavaScript developers at least know about JSON alternative, Ruby developers consider YAML, Java developers stick with XML for all cases (configuration, preferences, transport formats).

    So 90% of Java Joe-s never get to the static vs dynamic aproaches comparision while they take different direction on previous step — XML as the only possible serialization format. They will advocate that this is a “standard” solution and, ironically, they will be right — they refer to standard set by Sun+IBM+BEA+…numerous OSS projects.

    VS

  4. While Java developers do not consider XML as the only serialization format, XML is being abused in J2EE world as the only way of configuring multiple frameworks to make them work together. I’ve been writing on the subj on multiple ocasions, and this is the most recent article: http://java.sys-con.com/read/299903.htm

Comments are closed.