Saturday Mar 21, 2009

"To init or not to init - That is the question"

JavaFX has two block functions called "init" and "postinit". The JavaFX reference documentation defines these as "The initBlock is an optional block of code which is executed as the final step of instance initialization." and "The postInitBlock is an optional block of code which is executed after instance initialization has completed." Personally, I have found these to be confusing. When should I use an init block vs a postinit block? What is the state of the instance variables when these blocks are executed? How do they relate to super classes?

I wrote a simple program to explore some of these issues. This includes 3 classes, each extending the previous class.

class A {
    var a: Integer on replace {println("A.a = {a}");}
    init {
        println("A: init a = {a}");
    }
    postinit {
        println("A: postinit a = {a}");
    }
}

class B extends A {
    var b: Integer on replace {println("B.b = {b}");}
    init {
        println("B: init a = {a}, b = {b}");
    }
    postinit {
        println("B: postinit a = {a}, b = {b}");
    }
}

class C extends B {
    var c: Integer on replace {println("C.c = {c}");}
    override var a on replace {println("override C.a = {a}");}
    init {
        println("C: init a = {a}, b = {b}, c = {c}");
    }
    postinit {
        println("C: postinit a = {a}, b = {b}, c = {c}");
    }
}

C{
    a: 5
    b: 10
    c:15
};

When this program executes it prints out the following:

A.a = 5
override C.a = 5
B.b = 10
C.c = 15
A: init a = 5
B: init a = 5, b = 10
C: init a = 5, b = 10, c = 15
A: postinit a = 5
B: postinit a = 5, b = 10
C: postinit a = 5, b = 10, c = 15

What this shows is the order of initialization. First the instance variables from all the classes are initialized, followed by the "init" block for each class, then followed by the "postinit" block for each class. From this you can assume that first all variables are initialized starting with the top most super class followed by each subclass in order. Within a class the variables are initialized in the order they are declared. Once this is done then each "init" block is executed starting at the top most superclass, following down the class hierarchy. Then, in turn, the "postinit" blocks are called in the same way. It is important to understand this initialization order, so that when you define a class you understand when an item may be initialized and when it is not. 

Let's take the example of javafx.scene.control.Control  and javafx.scene.control.Skin. A Control has a one to one relationship to a Skin. As such, the Control has an instance variable "skin", and the Skin has an instance variable "control". Typically, you create the Skin object, then assign it to the control's skin variable using an override in your subclass.

public class MyControl extends Control {
    override var skin = MyControlSkin{};
}

The trigger for skin within the Control class assigns the Skin object to "skin" then sets the Skin object's "control" variable to itself. The point here is that, when MyControlSkin is first created, its "control" variable will be null until the Skin is fully initialized and has its "control" variable set. During the Skin initialization, you cannot access anything from the control.  The other point is that even if the Control has set the Skin's "control" variable, there is a window when the Control itself has not been fully initialized. Even if the Skin has a trigger on its "control" variable as in:

var MyControl = bind control as MyControl on replace {
     if(control != null) {
          // do initializaton from control
     }
}

you cannot assume that the control is fully initialized from this "on replace" block.

This condition is exasperated by the fact the you usually need to subclass control. However, if you use the 'override var skin" way to assign the Skin, this initialization will actually happen before any initialization is done in your Control subclass.  What I have found is that if I move the skin assignment to the postinit part of MyControl, then I can be assurred that MyControl is fully initialized when the Skin becomes aware of its existence.

Here are the two alternatives for assigning the Skin. First is the override version.

public class MyControl extends Control {
    public var myVar:Number;
    override var skin = MyControlSkin{}; // initialized before myVar
}

Second is the "postinit" version.

public class MyControl extends Control {
    public var myVar:Number;
    postinit {skin = MyControlSkin{}; }// initialized after myVar
}

The conclusion is you need to be aware of the order of initialization and how that impacts your class implementation.  If you get it wrong, the effects may be subtle and difficult to flesh out.

\*\*\* One should note, the goal of the JavaFX compiler team is to eliminate "postinit" and have "init" fully do the proper initialization. So expect "postinit" to be removed in a future release.



Thursday Mar 12, 2009

JavaFX - Gator JUG April 8, Gainesville FL, Orlando JUG - April 23, Orlando

I will be doing a live JavaFX development presentation for the Gator and Orlando Java User Groups in April. Details can be found at Gator JUG - Gainesville and Orlando JUG - Orlando .

Instead of the plain old slide presentation, I plan on showing live development of a JavaFX application using Netbeans IDE for JavaFX (thanks to Josh Marinacci for the inspiration). During the session, I will touch on some of the key features of JavaFX and demonstrate a simple JSON REST application. Lately, I have been contributing to the JFXtras project, and will also demonstrate some of those classes.

If you are in Central Florida, please plan on attending one of these sessions.

If you can't attend, I will be posting the sample application on our book's website JavaFX-Developing Rich Internet Applications sometime in April.

jim

Tuesday Jun 17, 2008

JavaFX Script and JSON Weather service

Brian Goetz recently completed the JavaFX script class "javafx.async.AsyncJsonCall" that allows one to issue a JSON webservice call in the background. Once the call is complete, a call back function "onDone" is called that indicates whether or not the call succeeded. This blog includes an example of how this JSON call might be done.

I decided to use the GeoNames JSON webservices and more particularily the "Weather Station with most recent weather observation". This service provides the current weather conditions at the particular weather station provided. The weather station is identified by the International Civil Aviation Organization (ICAO) code. Typically, in the US, this code is the letter 'K' followed by the more popular IATA code for the airport. For example, JFK international in New York has an IATA code of "JFK" so the ICAO code is KJFK. In the example below, I used my home airport of Orlando, Florida USA which has the ICAO code, "KMCO".

Click here for more details on on the GEO Names JSON Web Services and for the ICAO codes. For more information on the JSON grammar, check out www.json.org.

First, I declare a variable to hold the URL to the GEONames Web Service. Just to keep things simple for now I have hard coded in the ICAO code for Orlando, "KMCO".

"http://ws.geonames.org/weatherIcaoJSON?ICAO=KMCO"; 
Then, I declare an object to hold the values from the JSON Object that is returned. For now, I have to manually populate this object within my code, but eventually there will be mechanism in the JavaFX Script JSON framework to do this automatically.

Next, I declare the "AsyncJsonCall" object literal, passing in the "url", and providing a function implementation for the "onDone" attribute. "onDone" will be called once the call is completed and the "success" boolean parameter indicates whether it succeeded or not. If it failed,  the attribute "failureText" inside the "AsyncJsonCall" object will contain a string describing the failure.
If the call succeeds, then the "document" attribute  from the "AsyncJsonCall" object will contain the top level javafx.json.JSONObject. From this object,  I fetch the "weatherObservation" object, and from that, fetch all the members for my "weather" class.

To show the power of JavaFX Script binding, I draw a simple Frame, and output the members of my weather object. Because "AsyncJsonCall" runs in the background, there may be a delay before the actual data is displayed. Using the JavaFX binding feature, I bound the members of my "weather" object to the "Text" gui nodes, so that when the "weather" object eventually is populated, the screen will update automatically.

An example of the JSON Object that is returned:

{
  "weatherObservation":
    {
      "clouds":"scattered clouds",
      "weatherCondition":"n/a",
      "observation":"KMCO 171753Z 27010KT 10SM SCT045 SCT055 BKN140 BKN250 32/19 A2993 RMK AO2 SLP133 T03220189 10322 2024
4 58018",
      "windDirection":270,
      "ICAO":"KMCO",
      "seaLevelPressure":1013.3,
      "elevation":29,
      "countryCode":"US",
      "lng":-81.3333333333333,
      "temperature":"32.2",
      "dewPoint":"18.9",
      "windSpeed":"10",
      "humidity":45,
      "stationName":"Orlando, Orlando International Airport",
      "datetime":"2008-06-17 17:53:00",
      "lat":28.4166666666667
    }
}

The JavaFX Code to process the JSON Object returned from the JSON Weather Service:

 =============================

import javafx.async.\*;
import javafx.json.\*;
import java.net.URL;
import java.lang.System;
import javafx.gui.\*;

// ICAO code in US is typically 'K' + IATA airport code
// MCO is Orlando FL US IATA code, so ICAO is KMCO.
// New York - JFK is KJFK.
// See http://en.wikipedia.org/wiki/List_of_airports_by_ICAO_code
var url = "http://ws.geonames.org/weatherIcaoJSON?ICAO=KMCO";

class Weather {
    public attribute station:String;
    public attribute clouds:String;
    public attribute windDirection:Number;
    public attribute windSpeed:Number;
    public attribute temperature:Number;
    public attribute dewPoint:Number;
    public attribute humidity:Number;
    public attribute observation:String;
}
var weather:Weather = Weather{};
var call:AsyncJsonCall;
call = AsyncJsonCall{
    url: url 
    onDone: function(success : Boolean) : Void {
        System.out.println("JSON Call is done: result = {success}");
        if(success) {
            var json = call.document;
            var observation = json.getValue("weatherObservation") as JSONObject;
            var pair:Pair;
            weather.station = '{observation.getValue("stationName")}';
            weather.clouds = '{observation.getValue("clouds")}';
            weather.windDirection = observation.getPair("windDirection").getValueAsNumber();
            weather.windSpeed = observation.getPair("windSpeed").getValueAsNumber();
            weather.temperature = observation.getPair("temperature").getValueAsNumber();
            weather.dewPoint = observation.getPair("dewPoint").getValueAsNumber();
            weather.humidity = observation.getPair("humidity").getValueAsNumber();
            weather.observation = '{observation.getValue("observation")}';
        }else {
            System.out.println("failure = {call.failureText}");
        }
    }
}

Frame {
    title: "JSON Weather";
    closeAction: function() { java.lang.System.exit(0); }
    visible: true
    width: 500
    height: 500
    content: Canvas {
        content: VBox {
            spacing: 10
            content: [
                HBox {
                    content: [
                        Text {translateX:10 translateY:10 content: "Station:"},
                        Text {
                            translateX:10 
                            translateY:10 
                            content: bind weather.station 
                            fill: Color.BLUE
                        }
                    ]
                },
                HBox {
                    content: [
                        Text {translateX:10 translateY:10 content: "Clouds:"},
                        Text {
                            translateX:10 
                            translateY:10 
                            content: bind weather.clouds 
                            fill: Color.BLUE
                        }
                    ]
                }, 
                HBox {
                    content: [
                        Text {translateX:10 translateY:10 content: "Wind Direction:"},
                        Text {
                            translateX:10 
                            translateY:10 
                            content: bind "{weather.windDirection} degrees" 
                            fill: Color.BLUE}
                    ]
                },
                HBox {
                    content: [
                        Text {translateX:10 translateY:10 content: "Wind Speed:"},
                        Text {
                            translateX:10 
                            translateY:10 
                            content: bind "{weather.windSpeed} knots" 
                            fill: Color.BLUE}
                    ]
                }, 
                HBox {
                    content: [
                        Text {translateX:10 translateY:10 content: "Temperature:"},
                        Text {
                            translateX:10 
                            translateY:10 
                            content: bind "{weather.temperature}C degrees" 
                            fill: Color.BLUE}
                    ]
                }, 
                                HBox {
                    content: [
                        Text {translateX:10 translateY:10 content: "Dew Point:"},
                        Text {
                            translateX:10 
                            translateY:10 
                            content: bind "{weather.dewPoint}C degrees" 
                            fill: Color.BLUE}
                    ]
                }, 
                HBox {
                    content: [
                        Text {translateX:10 translateY:10 content: "Humidity:"},
                        Text {
                            translateX:10 
                            translateY:10 
                            content: bind "{weather.humidity}%" 
                            fill: Color.BLUE}
                    ]
                }, 
                HBox {
                    content: [
                        Text {translateX:10 translateY:10 content: "METAR Observation:"},
                        Text {
                            translateX:10 
                            translateY:10 
                            content: bind "{weather.observation}" 
                            fill: Color.RED}
                    ]
                },                 
            ]
        }
    }
    
}

=============================

There are a few other features of the  "javafx.async.AsyncJsonCall"  class that are useful, like showing the progress of the webservice call.  I will demonstrate these in a future blog post.


        
    
About

jimclarke

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today