Breaking OpenLaszlo loose from XML data

Java Development News:

Breaking OpenLaszlo loose from XML data

By Geert Bevin

01 Mar 2007 | TheServerSide.com

OpenLaszlo is a development platform that allows you to create rich internet applications which execute on several runtimes, including Flash and DHTML. You use an XML-based language, called LZX, to declaratively create the user interface and rely on EcmaScript to implement dynamic portions of the application. This is very similar to the traditional HTML and JavaScript duo.

Receiving non-XML data

OpenLaszlo usually expects your data to be provided in XML and many of its features, like UI data-binding, rely on this. However, there are situations where you would like to work with other data formats. JSON, for example, is increasingly being used as the data-interchange format for Ajax applications. You might want to consume an existing JSON-based service in OpenLaszlo or write your own.

Since the 3.1 release of OpenLaszlo, it's possible to perform data requests whose results aren't parsed as XML by using their XMLHttpRequest class. Before being able to use the XMLHttpRequest class you have to add the following include to the canvas of your OpenLaszlo application:

<include href="rpc/ajax.lzx"/>

The class behaves exactly as in classic DHTML Ajax applications, for example:

function doXhrRequest() {
  var url = "http:/yourservice"; // the single slash is mandatory in OpenLaszlo
  var req = new XMLHttpRequest();
  req.onreadystatechange = processRequestChange;
  req.open("GET", url, true);
  req.send(null);
}

This creates a new instance of XMLHttpRequest, registers the call-back function that will be executed as the request progresses, opens the request for a particular URL and sends it to the server.

The implementation of the call-back function is analogue to what you do in any other Ajax application:

function processRequestChange(request) {
  // check if the data transfer is complete
  if (4 == request.readyState) {
    // process the reponse text if the request was successful
    if (200 == request.status) {
      var text = request.responseText;

      // ... use the text ...
    }
  }
}

It's up to you to handle the received text in the manner that is best suited for your application.

Working with JSON

The rest of this article will create a very simple JSON service in Java and an OpenLaszlo client that consumes and displays it asynchronously.

Producing JSON with Java

I'm staying away from the server-side Java technology debate and assume that your framework of choice is able to output text with the text/plain mime-type.

The data that we'll be displaying is held by simple Person bean:

public class Person {
  private String name;
  private int age;

  public void setName(String name) { this.name = name; }
  public String getName() { return name; }
  public void setAge(int age) { this.age = age; }
  public int getAge() { return age;}
}

We will create a person instance and generate the JSON data for it. There are several Java libraries that are able to perform this task, but currently my choice goes to SOJO (Simplify Old Java Objects) because it's extremely simple to setup and use.

This is the code for the server-side JSON service in Java:

Person person = new Person();
person.setName("Geert Bevin");
person.setAge(32);

Object json = new JsonSerializer().serialize(person);
// output the json data as a string response
Parsing JSON in OpenLaszlo

OpenLaszlo doesn't ship with JSON capabilities, but luckily Oliver Steele wrote a library that does just this. You can obtain it from his personal website. Once downloaded and unzipped, you have to put the json.js file in your web directory and activate JSON capabilities by adding this tag to your OpenLaszlo canvas:

<script src="json.js"/>

Now you can parse the text that you received from the XMLHttpRequest with a simple function call and obtain an object structure that corresponds to the JSON that was sent by the server. This is done by adding the following line inside the processRequestChange function that we created above:

var result = JSON.parse(text);

The result variable is an associative array and you can obtain the values of the person bean properties like this:

var person_name = result.name;
var person_age = result.age;

Hooking this up to a GUI

To wrap this article up, we'll create a simple OpenLaszlo interface with a button that triggers the JSON service request when clicked and a couple of text fields that will display the received data.

Creating the controller and view

This can be declared by using the following snippet of LZX code inside your canvas tag:

<hbox inset="10">
  <vbox id="mainArea" inset="10" spacing="10">
    <button onclick="doXhrRequest()">Get data</button>

    <hbox name="personName">
      <text><b>Name:</b></text> <text name="value"/>
    </hbox>

    <hbox name="personAge">
      <text><b>Age:</b></text> <text name="value"/>
    </hbox>
  </vbox>
</hbox>

You'll notice that when the button is clicked, the doXhrRequest function, which we created at the beginning of this article, is executed.

To display the received data in the text fields, we simple have to change the text attributes of the correct view elements after we parsed the JSON in the processRequestChange function:

mainArea.personName.value.setAttribute("text", person_name);
mainArea.personAge.value.setAttribute("text", person_age);
Handling network latency graciously

Since the request will be executed asynchronously, it can take a while for the data to arrive after clicking the button, due to network latency. This can be frustrating for the user and give the impression that the application is slow or unresponsive. A good approach is to display a busy status indicator when the request doesn't finish instantly. In this example I'm going to overlay a slightly transparent glass pane that contains the message "Loading..." when the data takes longer that 200 milliseconds to arrive. The following LZX snippet creates the glass-pane:

<view name="loadingMessage" visible="false" bgcolor="#000000"
    opacity="0.7" width="100%" height="100%">
  <text align="center" valign="middle" fgcolor="#ffffff">Loading...</text>
</view>

By placing this code before the canvas closing tag, it will float above any other GUI element. Note that the 'visible' attribute has been set to false by default. We'll change that value when we want the glass pane to display.

So, as soon as the request starts, we initiate a timer that displays the glass-pane after 200 milliseconds. This can be done by relying on another value of the readyState attribute of the XMLHttpRequest class. You can refer to the class documentation for a complete overview of the possible values. For our use-case, we need to check if the attribute value is equal to 1, which means that the request has started. This additional condition is added to the beginning of the processRequestChange function:

// the request has been started
if (1 == request.readyState) {
  // create the delegate if is doesn't exist yet
  if ("undefined" == typeof(canvas.loadingMsgDel) ||
    !canvas.loadingMsgDel) {
    canvas.loadingMsgDel = new LzDelegate(canvas, "showLoadingMessage");
  }
  // intiate or reuse a timer
  LzTimer.resetTimer(canvas.loadingMsgDel, 200);
}

This code uses a delegate, which basically ties an event to a method. The event in question is the expiration of the timer. Here, the showLoadingMessage method will be called on the canvas instance after 200 milliseconds. The implementation of this method is easy, as you can see in the following snippet that should be added inside your canvas tag:

<method name="showLoadingMessage">
  this.loadingMessage.setAttribute("visible", true);
</method>

It simply makes the loading message visible.

Finally, we need to hide this message again when the data arrives or interrupt the timer if the request took less than 200 milliseconds. This is done by adding two lines to the initial condition inside the processRequestChange function:

  ...
  if (4 == request.readyState) {

    // disable a pending loading message or hide it if it's showing already
    LzTimer.removeTimer(canvas.loadingMsgDel);
    canvas.loadingMessage.setAttribute("visible", false);

    // process the reponse text if the request was successful
    if (200 == request.status) {
      ...

Conclusion

It's easy to process arbitrary text with the OpenLaszlo XMLHttpRequest class and even consume JSON services. Currently this is still primitive since it doesn't tie into the data-binding infrastructure. After the release of OpenLaszlo 4.0 (which should happen in Q1 2007), the dataset infrastructure should be better abstracted, creating a uniform approach to interacting with structured data. Until then you're still able to explicitly update the view elements that you need.

Resources