Wednesday 31 December 2008

Happy New Year

This e-card was written in JavaFX. The source code can be found here.

If you encounter problems seeing it, I am not surprised.

Related Posts:

Thursday 25 December 2008

Merry Christmas!

2008 has been a disastrous year in many aspects all over the world - from cyclone Nargis to the earth quake in SiChuan China; from Mumbai Massacre to mobocracy in Thailand... and let's not forget the global economic crisis...

Now more to home, following the down-sizing trend a company that I know of has decided to wind down the operation in Sydney just 2 weeks before christmas. As a result, people in the Sydney office have been made redundant. Come to think of it, the holiday season is perfect to fire people. Why? During Christmas, New Year and Australia Day (January 26) people's productivity is not at its peak. Firing them during this period will save money for the company left and right - it is a win-win situation for the company! Being a profit-chasing business entity, it is perfectly logical to 'do the right thing' in the holiday season. After all, humans are nothing but resources to a company - hence the term HR.

In the story, Mr Scrooge came to his senses and became a better human being in the end. But in real life, the reverse is usually true...

Saturday 20 December 2008

Turn Off 3G To Save The Environment!

My N95 battery could never last more than 16 hours even when sitting idle. I had to recharge it every night. This week when I was in Kathmandu, I was pleasantly surprised: my battery actually lasted over 36 hours during which I snapped more than 70 photos , played 20 minutes of music and had a couple of phone calls (totaling over 15 minutes of talk time maybe). I noticed that there was no 3G network in Nepal (or at least I was not roamed to one). I believe that is why my battery lasted so long. That fact that I live on the edge of the cell probably exacerbated the situation.

So I decided to disable 3G on my phone. Normally, this is a straight forward operation available from the Settings -> Phone menu. However, my phone was from 3 Australia (product code: 0547465) and it was branded - meaning that it was customised for 3 Australia: some 3 applications was installed, some menu items have been disabled... and the menu item to turn off 3G has been disabled. Super keen on saving the environment by extending my battery life I set out to debrand the phone and turn off 3G!

The process of debranding is as following:

  1. Backup the phone using PC Suite
  2. Change the product code by following the instructions. The new product code I used is 0536087. Note that the NSS could not detect any phone on my notebook. In the end I installed NSS on my desktop (also running XP) using root account and that all worked fine. I don't really know why it would not work on my laptop.
  3. Update the firmware using Nokia Software Updater
  4. Restore the phone using PC Suite. The restore is not perfect and I still have to re-arrange/reinstall some applications. And all my messages and contacts are lost!
But it was all worth it. Now under the Tools -> Settings -> Phone -> Network menu I have an extra item: Network mode where I can select GSM only!

On the negative side, there are some major drawbacks upgrading to the 30.x version of the firmware. TRK no longer works and I had to install alternative (but better) solution. Fontrouter does not seem to work any more in the new browser (because it does not have the character encoding that I need) and I got around the problem by installing Opera Mini instead.

Tuesday 16 December 2008

The Sorry State of JavaFX 1.0

The release of JavaFX 1.0 officially marks Sun's defeat in the RIA battle. Why? Because JavaFX 1.0 is inferior to its competitors in all major battlefronts of the RIA war. It looks like a rushed job to make a marketing deadline.

The JavaFX 1.0 APIs

The JavaFX 1.0 is shipped with 2D graphical capabilities and that's pretty much it. Even basic things such as 3D graphics, faster video codecs are labeled with 'coming soon...' on their demo site. Forget about building any business RIAs because there are only a handful of simple Swing widgets at your disposal. On the other hand, people have been using Flash to build business applications for years; Silverlight has got 3D capabilities since v1.0.

Design/Development

The JavaFX plugin for NetBeans is a joke - it's buggy, sluggish and poor in features. One mistyped word or bracket can result in red squiggly lines all over the screen; then it produces unhelpful error hints such as 'sorry I was expecting so and so but I saw such as such...' so that the user has to read through the whole sentence several times and still hard to figure out what the complaint is. What happens to KISS principle?

Sure, Sun had said that they intended the plugin to be used by designers also - hence the 'user friendly' error messages. But let's get serious, how can anyone assume graphical designers will be using computer programming language to define simple things such as Timelines? Sun doesn't have to look far to find a better way - Microsoft has Expression Web to be used by graphical designers, and VS.NET 2008 to be used by programmers when it comes to Silverlight design and development workflow. (The JavaFX plugin for Illustrator is only good for editing shapes).

Deployment

To me there is not much point of deploying RIA as a thick client using Java Web Start. I am only interested in running RIA in a browser. With the new way of deploying applets in Java, JNLP is used, so that the HTML file points to a JNLP file and the JNLP file points to the JAR files and more JNLP files... It's a bit like my multi-hop flight from Sydney to Kathmandu (via Bristane then Bangkok) that took me over 20 hours and 6 of which was waiting at BKK for transit (that that is how I have the time to write this post) all because my company was too cheap to book the Qantas flight.

This is unnecessarily complicated because Sun doesn't want to have JavaFX plugin to the browser. On the other hand, Flash/Silverlight plugins are installed onto the browser so that all you need to specify is the object or embed tag in the HTML file without the hassle of running the myriad javascripts to detect/download the additional framework JAR files every time.

The User Experience

If you are using a browser that does not have the latest Java plugin (that supports JavaFX) installed (such as on my Nokia N95, or using the PC in the Thai Airways lounge at Kathmandu international airport), then you will be greeted with a message box forcing you to redirect to Sun's download website. There is no option to cancel!

Moreover, those javascripts do not report any errors if something goes wrong - it just hangs there with the Java logo spinning around and around forever...

Wednesday 10 December 2008

Merry Christmas JavaFX 1.0

JavaFX 1.0 was finally released last Friday. After over 2 years of brooding, I wonder what Sun has come up with - oh boy what a disappointment the JavaFX IDE (NB plug-in) is! But that is for another post.

The JavaFX 1.0 is no good for developing any RIA business applications for its lack of widgets. Instead it is OK for little toys such as those demos published on javafx.com. Immersed in the holiday season atmosphere, I implemented my Christmas card in JavaFX. A screenshot is shown below.

The Christmas card has the following features:

  • Transformation - the red and yellow text fly into the screen using a combination of Translation, Rotation and Scale.
  • Media playback - background music (mp3 file) from Michael Buble - Let it snow.
  • Animation - snow fall simulation
  • Drag out - when run as applet in a browser, the card can be dragged out of the browser.

The Project Structure

The project was created using NetBeans 6.5 with JavaFX plug-ins (installed from within NetBeans). I created a JavaFX project called MerryChristmas. The project structure is shown below:

The only files that I added are:

  1. LetItSnow.mp3 - the background music song
  2. tree.png - the background image: photo of Christmas tree shot at Darling Harbour on December 8, 2008.
  3. Main.fx - the main JavaFX file containing the scene and its contents
  4. SnowFall.fx - the CustomNode simulating snow fall and snow flakes (using circle)

The Main Contents

The contents of the main scene are two Texts, one background image, one background music and the snow fall (implemented as CustomNode).

The texts and their animation (actually transformation) are created as shown below. Notice the transforms which use binding data changed by the Timeline definition.

...
var x:Number;
var y: Number;
var scaleX : Number;
var scaleY:Number;
var angle:Number;

Timeline {
    repeatCount: 1
    keyFrames: [
        at (0s) {x=>180; y => 250; scaleX => 0.0; scaleY => 0.0; angle => -180},
        at (10s) {x=> 0; y => 0;
        scaleX => 1.0; scaleY => 1.0;
        angle => 0
        tween Interpolator.EASEOUT},
    ]
}.play();
...
var lighting = Lighting{
    light: DistantLight{
        azimuth: 60,
        elevation: 70
    }
    surfaceScale: 5
};

Stage {
    ...
    scene: Scene {
        content: [
            ...
            Text {
                content: "Merry Christmas & \nHappy New Year!"
                font: Font.font("Arial Bold", FontWeight.BOLD, 40)
                textOrigin: TextOrigin.TOP
                textAlignment: TextAlignment.CENTER
                x: 10
                y: 20
                transforms: bind[
                    Translate{
                        x:x
                        y:y}
                    Scale{
                        x:scaleX
                        y:scaleY}
                    Rotate{
                        angle: -angle
                        pivotX: 180
                        pivotY: 20 }
                ]
                fill: Color.RED
                effect: lighting
            },
            Text {
                content: "From Romen"
                font: Font.font("Times", FontWeight.BOLD, 40)
                textOrigin: TextOrigin.TOP
                x: 90
                y: 470
                transforms: bind[
                    Translate{
                        x:x
                        y:y}
                    Scale{
                        x:scaleX
                        y:scaleY}
                    Rotate{
                        angle: angle
                        pivotX: 180
                        pivotY: 450}
                ]
                fill: Color.YELLOW
                effect: lighting
            },
...

Any suggestions on how to make unicode text work are welcome.

The background image and music are easy enough:
...
            ImageView {
                image: Image {
                    url: "{__DIR__}tree.png"
                }
                x:0,
                y:0
            },
...
            MediaView {
                mediaPlayer: MediaPlayer {
                    autoPlay: true
                    media: Media {
                        source: "{__DIR__}LetItSnow.mp3"
                    }
                }
            },
...

Drag Out

To enable drag out when run as an applet in browser, I added the following extension to the Stage:
    extensions: [
        AppletStageExtension {
            shouldDragStart: function(e): Boolean {
                return e.primaryButtonDown;
            }
            useDefaultClose: false
        }
    ]
Also I had to specify in the Project's properties dialog and check the Draggable checkbox so that it will generate the .html file with the draggable set to true. The generated .html file snippet:
...

...

Snow Fall

All snow related classes are in the SnowFall.fx file. I used white circle as the snow flake:
public class SnowFlake extends Circle {
    init {
        fill = Color.WHITE;
        radius = 3 + Math.random() * 3;
        opacity= Math.random() * 0.5 + 0.5;
    }
}
The SnowFall class takes 3 attributes:
  • height, width - these specify the size of the canvas so that all the snow flakes can spread out in the canvas making it more realistic
  • numberOfFlakes - the number of snow flakes to be drawn and animated. The more flakes, the more threads JavaFX will have to create and manage under the hood.
The animation of the snow flakes are a bit tricky. Initially all the snow flakes are generated and randomly placed around the canvas; then, they start to fall. The movement of the snowflakes are as such: every second the snow flake will move left (negative) or right (positive) by a random amount, and move down by a random amount - i.e. the horizontal movement (x) can be either positive or negative, the vertical movement (y) is positive only. This is shown below:
                            KeyFrame {
                                ...
                                values: [
                                    x =>
                                    x + (Math.random() - 0.5)
                                    y =>
                                    y + Math.random() tween Interpolator.LINEAR
                                ]
...
When the snow flake hits either side of the boundary, it sort of bounces back a little; when it moves off the bottom of the canvas, it is wrapped around and re-appears at the top of the canvas and falls down again. This is achieved by using action of Keyframe which are executed at the beginning of each key frame.
...
                               action: function() {
                                    if(flake.centerY+y >height) {
                                        y=0; flake.centerY=0;
                                    }
                                    if(flake.centerX+x>width) {
                                        x=0; flake.centerX=width;
                                    }
                                    if(flake.centerX+x<0) {
                                        x=0; flake.centerX=0;
                                    }
                                }
...
The SnowFall class generates all the snow flakes in a for loop. Each snow flake is accompanied by a Timeline controlling its animation. The number of frames in each Timeline is also randomised (between 60 and 120 seconds per cycle), to avoid the situation where all snow flakes suddenly reset at the same time.
for ( i in [1..numberOfFlakes]) {
...
                    def timer = 60+60*Math.random();
                    Timeline {
                        repeatCount: Timeline.INDEFINITE
                        keyFrames: [ for (j in [0..timer]) {
                            KeyFrame {
...
Want to see the Christmas card in action? Well, you will have to wait till Christmas! [Update NYe2008]Now that it is NYE 2008, click here to run the e-card.

Meanwhile, here are the complete source code.

SnowFall.fx

/*
 * SnowFall.fx
 *
 * Created on 10/12/2008, 09:55:19
 */

package merrychristmas;

import java.lang.Math;
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.animation.KeyFrame;
import java.lang.System;

/**
 * @author ROMENL
 */

public class SnowFall extends CustomNode {
    public var height:Number;
    public var width:Number;
    public var numberOfFlakes:Number;

    override function create():Node {
        return Group {
            content: [
                for ( i in [1..numberOfFlakes]) {
                    var x:Number;
                    var y:Number;
                    x=1;
                    y=1;
                    var flake = SnowFlake{
                        centerX: Math.random() * width
                        centerY: Math.random() * height
                        translateX: bind x;
                        translateY: bind y;
                    }

                    def timer = 60+60*Math.random();
                    Timeline {
                        repeatCount: Timeline.INDEFINITE
                        keyFrames: [ for (j in [0..timer]) {
                            KeyFrame {
                                time: bind Duration.valueOf(j * 1000)
                                values: [
                                    x =>
                                    x + (Math.random() - 0.5)
                                    y =>
                                    y + Math.random() tween Interpolator.LINEAR
                                ]
                                action: function() {
                                    if(flake.centerY+y >height) {
                                        y=0; flake.centerY=0;
                                    }
                                    if(flake.centerX+x>width) {
                                        x=0; flake.centerX=width;
                                    }
                                    if(flake.centerX+x<0) {
                                        x=0; flake.centerX=0;
                                    }
                                }
                            }
                        }
                        ]
                    }.play();
                    flake
                }]
            
        };
    }
}
public class SnowFlake extends Circle {
    init {
        fill = Color.WHITE;
        radius = 3 + Math.random() * 3;
        opacity= Math.random() * 0.5 + 0.5;
    }
}

Main.fx

/*
 * Main.fx
 *
 * Created on 9/12/2008, 15:29:24
 */

package merrychristmas;

import javafx.animation.*;
import javafx.scene.effect.*;
import javafx.scene.effect.light.*;
import javafx.scene.image.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.scene.paint.Color;
import javafx.scene.Scene;
import javafx.scene.text.*;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate;
import javafx.stage.AppletStageExtension;
import javafx.stage.Stage;

/**
 * @author ROMENL
 */
var width=390;
var height=540;
var x:Number;
var y: Number;
var scaleX : Number;
var scaleY:Number;
var angle:Number;

Timeline {
    repeatCount: 1//Timeline.INDEFINITE
    keyFrames: [
        at (0s) {x=>180; y => 250; scaleX => 0.0; scaleY => 0.0; angle => -180},
        at (10s) {x=> 0; y => 0;
        scaleX => 1.0; scaleY => 1.0;
        angle => 0
        tween Interpolator.EASEOUT},
    ]
}.play();

var lighting = Lighting{
    light: DistantLight{
        azimuth: 60,
        elevation: 70
    }
    surfaceScale: 5
};

Stage {
    title: "Merry Xmas JavaFX"
    width: width
    height: height
    scene: Scene {
        content: [
            ImageView {
                image: Image {
                    url: "{__DIR__}tree.png"
                }
                x:0,
                y:0
            },
            Text {
                content: "Merry Christmas & \nHappy New Year!"
                font: Font.font("Arial Bold", FontWeight.BOLD, 40)
                textOrigin: TextOrigin.TOP
                textAlignment: TextAlignment.CENTER
                x: 10
                y: 20
                transforms: bind[
                    Translate{
                        x:x
                        y:y}
                    Scale{
                        x:scaleX
                        y:scaleY}
                    Rotate{
                        angle: -angle
                        pivotX: 180
                        pivotY: 20 }
                ]
                fill: Color.RED
                effect: lighting
            },
            Text {
                content: "From Romen"
                font: Font.font("Times", FontWeight.BOLD, 40)
                textOrigin: TextOrigin.TOP
                x: 90
                y: 470
                transforms: bind[
                    Translate{
                        x:x
                        y:y}
                    Scale{
                        x:scaleX
                        y:scaleY}
                    Rotate{
                        angle: angle
                        pivotX: 180
                        pivotY: 450}
                ]
                fill: Color.YELLOW
                effect: lighting
            },
            MediaView {
                mediaPlayer: MediaPlayer {
                    autoPlay: true
                    media: Media {
                        source: "{__DIR__}LetItSnow.mp3"
                    }
                }
            },
            SnowFall {
                height: height
                width: width
                numberOfFlakes: 30
            }
        ]
    }
    extensions: [
        AppletStageExtension {
            shouldDragStart: function(e): Boolean {
                return e.primaryButtonDown;
            }
            useDefaultClose: false
        }
    ]
}
Related Posts:
  1. Happy New Year
  2. Happy New Year of the Ox
  3. The Sorry State of JavaFX 1.0

Thursday 4 December 2008

The Long Tail Theory

In the Telco business we often talk about the long tail products/services illustrated in the following diagram.

The idea is that for a service provider to stay competitive, it has to deploy and experiment with a large number of products and see how they are accepted by the market. There will be a small number of popular ones and the rest will be less popular as indicated in the long tail. So, over time, the service provider will kill the proven unpopular products and replace them with new/other products. You may wonder, why do they bother with the long tail since they are not that popular anyway? There are two major reasons:

  1. The service providers are too paranoid not to provide those services because they want to stay competitive - anything that their competitors have, they also want it.
  2. Other long tail graphs replace the Popularity label with Revenue. It's not hard to see that the total area under the Long Tail curve (i.e. total revenue) could add up to an handsome amount.

How is this theory relevant to me? Well, being an IT professional, I am also paranoid about my knowledge and skills being stale. So I have to invest my time and effort on updating and expanding my knowledges constantly. With all these new technologies and frameworks popping up all the time, I feel like a headless chook all the time.

The best way to learn a new language/framework/library is to actually use it. 'Hello World' just doesn't cut it. Yet a full blown application such as Web Mail or Pet Shop may be too big or too long for the purpose of learning or proof of concept. That is why I have my own little pet project - Address (among others), which serves as the use cases for my learning and experimentation.

For the products that I like and feel useful in my work, I will keep them in my toolbelt and probably move them up to the head. For the ones that are irrelavent to me, I drop them from the Long Tail picture altogether. There are also products that have died (e.g. Orion Server) or dying and being replaced by others (e.g. GWT-Ext) in the game of evolution. But it does not mean that my time spent on them were wasted. I still learn and accumulate a great deal of experiences so that when I encounter a new product, I can always draw on these experiences and view it in a better light.

Monday 1 December 2008

GWT vs. ZK - SLOC Count

I often read about how compact the ZK code was compared to GWT. I couldn't quite believe it. I thought it must have been different design patterns and style of coding used. So I decided to do it myself - I implemented my pet project in both GWT-Ext (GWT 1.4.6 and GWT-Ext 0.9.3 in December 2007) and ZK (ZK 3.5.1 with ZK Studio 0.9.0 in November 2008) and compared the Source Line of Code (SLOC) counts using Code Count tool.

Before I show the SLOC comparison, I need to give a brief introduction of the little demo program that I wrote. The program is a GUI to show various capabilities of the Address package, which has a service tier and DAO implementations behind it. The Address package provides a meta-data driven way of managing address records (see here for the data model). The concerned application here is only the GUI portion. The GUI has the following screens (left=ZK, right=GWT):

CRUD

This screen is used for Create, Retrieve, Update and Delete (CRUD) of the Address records from/to the database.

Find

This screen is used to call the corresponding Address services to find addresses either by keywords or by location.

Compare

This screen is used to call the corresponding Address services to compare two given Address records. Note that the ZK implementation supports drag-and-drop - i.e. I can drag a row from the list of addresses and drop it onto the text box and the text box will be populated with the content of the address (as a string). On the other hand, the GWT implementation does not support DnD because at the time of writing it, GWT-Ext did not support this kind of DnD yet (and I am not about to rewrite it using new version of GWT-Ext now considering Sanjiv has left the project... but that's another story).

Preferences

This screen is used to manage user preferences of the GUI application. Cookies are used as the storage.

In my SLOC count, I divided the source code into the following groups:

  1. Main View - this includes the main screens: CRUD, Find and Compare tab panels.
  2. Preference View - the user preference window
  3. Model/Controller - the code behind the screen components, including models, event handlers, interaction with the service tier, helper/utilities
  4. Splasher - this is the Splasher screen displayed at the beginning of the application. Only ZK version has implemented this (using index.zul and automatically forwarding to the main view after initialising the Address service); other versions do not have this screen.

Note that in ZK, all the views are a result of drag-and-drop from the components palette with modifications by hand.

The physical and logical SLOC counts are shown below.

The results speak for themselves.

UPDATE [2008-12-08]:

GWT/GWT-Ext screenshots have also been added after the ZK ones (i.e. GWT screenshots are on the right-hand side).