Laszlo: An Open Source Framework for Rich Internet ApplicationsBeing a Java programmer sometimes feels like being in the movie Groundhog Day. In the movie, Bill Murray is stuck in a loop: every morning when he wakes up it's the same day (Groundhog Day). He relives the day over and over again, seemingly without reason. But the days are subtly different: each time he relives the same event, he learns a little bit more about how to be a better person. And, at the end of the movie, Bill Murray finally gets to move on and live the rest of his life.
Similarly, every year, without fail, Java developers get to see new programming frameworks and container models. And, in much the same way that Bill Murray gradually addresses his problems and becomes a better person, the frameworks are getting better, and helping us to become better programmers (or, at least, deliver higher quality applications in shorter time frames). But, in the same way that Bill Murray keeps seeing the same things happening over and over again, we keep getting presented with frameworks that keep solving the same problems over and over again. While JavaServer Faces looks much better than Struts, it doesn't enable anything new: it's just a better way of creating the same old HTML-over-HTTP web applications.
But just as Bill Murray eventually got to experience all of the days that come after Groundhog Day, we may be on the verge of changing how we build web applications. There's a growing restlessness in the air, and a growing movement towards rich internet applications. In this article, I'm going to give you a quick overview of Laszlo, an open source rich internet application development platform. First I'll give a high level overview of what Laszlo is, and how it works. Then I'll give you a quick tour through some of the basic features of Laszlo, and talk about what's involved in building an application in Laszlo. And, after that, I'll talk about where it works best, and when it makes sense to use Laszlo.
The phrase "rich internet application framework" is a lot like the word pornography: easy to identify (the old "I know it when I see it" standard) but hard to define. Generally speaking, when people say "rich internet application," they usually mean something that meets the following criteria:
You'll note that the above list of criteria doesn't include a lot of technological guidance. That's because the field of rich internet applications is wide open, and there are a lot of frameworks and environments out there. Some of the leading contenders include:
However, while they use vastly different technologies, platforms for rich internet architectures have some interesting commonalities. Among them are:
Laszlo Systems was founded in 2000 to "advance the web experience." In the early years of the company, "advancing the web experience" meant building and selling a Flash-based rich internet application framework to large enterprises. But in mid-2004, Laszlo went open source, and Laszlo is now a mature and powerful framework for rich internet applications that also happens to be free. And that's pretty compelling.
The Laszlo platform consists of two pieces: a declarative XML-based language called LZX that is used to write client applications, and an application server (and LZX compiler) called the presentation server.
The presentation server plays two different roles in a production Laszlo system. The first is that it compiles LZX applications into Flash movies when the applications are requested by a browser. In Laszlo, client-side deployment is the standard web model: to launch an application, the user loads a web page. The web page contains an object tag, which launches the Flash movie. The presentation server also maintains a cache of the compiled LZX programs (it only recompiles LZX programs when they have changed).
The presentation server also brokers data requests from client applications (basically, it forwards the requests to your server-side code). This was originally part of the architecture because of security restrictions inherent in Flash movies when Laszlo was originally architected, and will probably be removed in a future version of Laszlo.
LZX, the language that Laszlo programs are written in, is a hybrid language that looks a lot like XHTML. LZX has an XML tag syntax to define pages and user interface constructs, and uses JavaScript for procedural logic and state variables. This has two significant benefits:
In a nutshell, developers write applications in a combination of LZX (for the client application) and some server-side code. The presentation server takes the LZX and compiles it into Flash movies, which are sent to the web browser.
The choice of Flash as a delivery vehicle for rich internet applications is an interesting one. Many people think of Flash as either eye candy for the Web or as the delivery mechanism for silly, yet addictive, games. What's more, a sizable portion of the Web's most influential people think of it as actively bad. But Flash, especially Flash 5 (which Laszlo uses), has some very strong advantages.
Actually finding Laszlo on the Web can be somewhat confusing; there are a lot of sites out there, each of which contains a small part of the big picture. Here are some of the websites you should visit if you want to learn more:
Major Sites:
Relevant Weblogs:
Interesting Additional Components and Code:
|
LZX is a whole new language, and the architecture of a typical Laszlo application is very different from the architecture of a typical web application. It's impossible for me to take you through all of Laszlo, and explain how it fits together in detail. But by walking through part of a simple application, I can give you a good feel for how the language holds together, and what it's capable of (see the sidebar Getting Started Quickly (on Windows)).
The application I'll use is called Oranges. It does the following:
This isn't a complicated application. But it does have three screens (the login screen, the failure screen, and the data screen), some screen transition logic, and some user-specific data. Which means it's a fine way to demonstrate some of Laszlo's capabilities.
We'll start by designing our login page. Login screens have several properties: they're centered in the screen, they ask for a name and password, and they can't be closed or circumvented. Here's the Flash generated by the LZX compiler for a simple login screen:
This is a very simple screen, but it already illustrates some fairly important points about Laszlo. For example, if you type in your name and then hit Tab, you'll see an animation visually guide you to the next field. That's part of what Laszlo CTO David Temkin refers to as a cinematic user experience--users get visual cues and prompts that help them know what to do next.
Another important point is that you can click on the title bar and drag the window around. We've all become used to things like clicking and dragging in our desktop applications; an important part of rich internet applications is that they enable the same types of user interface actions in our web applications.
I also want to point out that I'm not showing you screenshots in this article; all of the examples in this article are actual applications compiled from LZX and embedded in this article using the object tag. In fact, here's how I embedded the login screen above:
<object type="application/x-shockwave-Flash" data="example_one.swf" width="800" height="400">
<param name="movie" value="example_one.swf" />
</object>
The login screen above was pretty easy to write. In order to create it in in LZX, I simply used LZX's modaldialog class and added some text fields. Here's the complete LZX program for the first login screen:
<canvas bgcolor="#335577">
<modaldialog name = "login_dialog" width="300" height = "200" title = "Please Login">
<form>
<view width="200">
<reverselayout axis="x" spacing="10"/>
<edittext></edittext>
<text>Name:</text>
</view>
<view width="200">
<reverselayout axis="x" spacing="10"/>
<edittext password="true"></edittext>
<text>Password:</text>
</view>
<button text="Login" onclick="login()"/>
</form>
</modaldialog>
<script>
login_dialog.open();
function login() {
// we'll implement this later
}
</script>
</canvas>
The most important point about this code is that it's at least as readable as HTML. LZX is a very readable programming language, and it's designed to look as much like HTML as possible (the button even has an onclick method). UI design in Laszlo is a lot like XHTML with a richer set of user interface controls and no cascading style sheets.
But even though the code is very readable, there's a lot going on in it. For example:
canvas node. LZX programs are XML documents, and hence they have to have a single root node, which in LZX is the canvas node. The fact that LZX programs are well-formed XML has several obvious advantages. But it's also a little problematic, in that the entire LZX program executes inside a single canvas. You can have multiple "window" objects, but they're all constrained to live in one canvas (and, hence, inside of a single browser window).main function; instead, any "top-level" scripts (scripts that are direct children of the canvas node) are executed when the program loads.form to hold several views. An LZX Form is a lot like an HTML form, and is used in similar situations. view inside the form used a reverselayout to organize its fields. Like Java, LZX uses layout managers to arrange components.The next step in building Oranges is adding behavior to the login page. Remember that, as much as possible, we want the program to execute on the client side, without sending a large number of messages to the server. This means that instead of having the entire browser reload the page every time the application needs to show a new page, I'm going to define all of the pages inside of a single LZX application (which will be sent to the client as a single Flash movie) and write code to perform the page transitions myself.
I'm going to do this in stages. The first stage is simply adding event handlers to our form, and having it check whether the login is valid. As part of doing this, I'm going to turn both our login form and our login dialog into classes, and add methods to them. In the next version of Oranges, if you type secure as the password, you get one message. If you use any other password, something else happens.
Before I show you the code, though, here's the Flash generated by Laszlo for our new login panel:
The code for this version is very similar to the code for the first version. But, since we're exploring LZX, I've refactored it to take advantage of LZX's support for object-oriented programming. Rather than start by discussing the LZX object model, I'm going to show you the class definitions for the new login form and login panel, which I'm storing in a separate file named loginpanel.lzx. Here they are:
<library>
<class name ="loginform" extends="form">
<view width="200" name="idview">
<reverselayout axis="x" spacing="10"/>
<edittext name="name"></edittext>
<text>Name:</text>
</view>
<view width="200" name="passwordview">
<reverselayout axis="x" spacing="10"/>
<edittext name="password" password="true"></edittext>
<text>Password:</text>
</view>
<button text="Login">
<method event="onclick">
var name = this.parent.idview.name.getText();
var password = this.parent.passwordview.password.getText();
login(name, password);
</method>
</button>
<method name="clear">
idview.name.setText("");
passwordview.password.setText("");
</method>
</class>
<class name="logindialog" extends="modaldialog"
width = "300"
height = "200"
title = "Please Login"
x = "50"
y = "50">
<simplelayout axis="y" spacing ="10"/>
<text name="failureMessage" fgcolor="red" width="280"> </text>
<loginform name="loginform"/>
<method name="setMessage" args="message">
failureMessage.setText(message);
</method>
</class>
</library>
In this code, instead of using canvas as the top-level tag, I used the library tag. The library contains two classes: loginform and logindialog. As before, LZX is very readable; most programmers can read the code and get a good idea of what's going on. loginform is a subclass of form that contains a pair of views (named idview and passwordview) and a button that doesn't have a name, but does have an onclick method.
The definition of logindialog is equally transparent: it's a modal dialog that contains a text field for messages and a login form. It also encapsulates how messages are displayed by defining a setMessages method.
As this example shows, defining classes in LZX is simply a matter of using the class tag. Most of the things you'd expect to be true are true: classes can extend other classes (including classes provided by Laszlo Systems), can contain embedded components and methods, and so on.
However, what LZX means by "class" is subtly different from most programming languages. LZX uses a prototype-style object model, where instances are (basically) created as copies of the class definition. To see what that means, consider the following two instances of logindialog:
<logindialog name = "standard_login_dialog" />
<logindialog name = "dysfunctional_login_dialog">
<method name="setMessage" args="message" />
</logindialog>
Both login_dialog and dysfunctional_login_dialog are instances of logindialog (when we defined the logindialog class, we implicitly defined the logindialog tag as well). But dysfunctional_login_dialog overrides the definition of setMessage defined in logindialog. When we override a method definition on an instance, we're not deleting the old method definition, or even changing the behavior of any other instance. We're simply changing the behavior of a single instance. This is something that's not possible in more traditional languages like Java.
Note also that we didn't name the button in
loginform. Logically, this is a lot like defining an instance variable with no name, and that's something that you can't normally do in an object-oriented language.
Another interesting aspect of LZX is illustrated by the login button in loginform.
<button text="Login">
<method event="onclick">
var name = this.parent.idview.name.getText();
var password = this.parent.passwordview.password.getText();
login(name, password);
</method>
</button>
The onclick function references a JavaScript function named login that isn't defined in the library at all. This simply becomes a call to a global function, defined in a <script> block inside of the main program. The presentation server will bind the call when it builds the Flash movie; until then, there's no way of knowing whether the function being referenced exists.
Now that we've defined our classes, it's time to look at the actual program. Here's the code:
<canvas bgcolor="#335577">
<!-- Most of our custom objects are defined in library files -->
<include href="loginpanel.lzx"/>
<!-- Define some visual objects -->
<logindialog name = "login_dialog" />
<!-- Define the login function -->
<script>
<![CDATA[
function startApp() {
login_dialog.open();
}
function login(name, password) {
if (invalidLogin(name, password)) {
login_dialog.setMessage("Dude, That Was Both Lame <b>and</b> Bogus")
}
else {
login_dialog.setMessage("You logged in. <b>Awesome!</b>");
}
}
function invalidLogin(name, password) {
if (password=="secure") {
return false;
}
return true;
}
]]>
</script>
<!-- Let's get the party started -->
<script>
startApp();
</script>
</canvas>
This is really straightforward code: we include the library, we create an instance of logindialog, and we define the login function it requires. But somehow, out of that, we've got fairly rich behavior, in a browser-based application, using an object-oriented programming language. That's pretty neat.
|
The next step in building Oranges is to add in a second screen, which tells the user how many oranges they've eaten. In order to do this, I'm going to take advantage of another one of LZX's features and use an XML dataset. In LZX, information is usually stored in datasets, and then accessed via XPath. In fact, datasets are important enough that there are two distinct ways to interact with them in LZX: either via Datapaths or Datapointers.
For starters, here's what an XML dataset looks like:
<dataset name="people">
<people>
<person>
<loginid>dsteinberg</loginid>
<firstname>Daniel</firstname>
<lastname>Steinberg</lastname>
<numberoforanges>124</numberoforanges>
<password>secure</password>
</person>
<person>
<loginid>chromatic</loginid>
<firstname>chromatic</firstname>
<lastname>NA</lastname>
<numberoforanges>10089</numberoforanges>
<password>reallysecure</password>
</person>
</people>
</dataset>
Strictly speaking, this is a local dataset; it contains all of its information, instead of simply containing a reference to the information. The structure is simple, though: you use the dataset tag and then, within it, store data in XML format.
At this point, if you're an experienced DHTML programmer, you might be thinking, "Why aren't we using JavaScript arrays and variables to hold information? After all, we are building web applications, and in DHTML applications, client-side application state is usually stored in JavaScript variables."
The answer is that datasets are simpler, cleaner, and more standardized. Using datasets involves deciding on a representation. (In the example above, loginid is stored in a subtag of person instead of as an attribute. Why? Because that's the way it's done in the example.) But everything except the particular choice of representation is done in a standard way. This is in stark contrast to DHTML and JavaScript, where it seems that every web application does things slightly differently.
The information inside of an XML dataset is manipulated via XPath (note that while it doesn't support all of XPath, LZX supports a fairly large subset of XPath). While a complete discussion of XPath is well outside of the scope of this article, it's fairly easy to use basic XPath expressions by following a few simple rules.
:).@ stands for attribute.text() means "give me the text associated to a node."Thus, in the above dataset, we have:
people:person/loginid/text() returns dsteinberg.people:person[1]/loginid/text() returns dsteinberg.people:person[2]/loginid/text() returns chromatic.Given a dataset, there are two basic ways to display it using LZX. The first way is to use implicit binding. The idea here is that you attach part of a path to a view (or other user interface component) as an datapath. LZX is smart enough to iterate over all of the nodes matching the datapath, and repeatedly render the component, binding the contents of the component to a different node each time. Thus, for example, the following Flash movie:
Was generated by the following LZX.
<view name="table">
<simplelayout axis="y" spacing="10"/>
<view datapath="people:/people/person">
<simplelayout axis="x" spacing ="10"/>
<text datapath="loginid/text()" />
<text datapath="firstname/text()" />
<text datapath="lastname/text()" />
<text datapath="numberoforanges/text()" />
</view>
</view>
The way to read this is that LZX iterates over the view once for each top-level node matching the partial path people:/people/person. During each iteration, Laszlo inserts a number at the end of the partial datapath, and then the text nodes inside of the view complete the path. For example, on the first iteration, the view has the path people:/people/person, the LZX runtime inserts a [1], and then the first text node appends loginid/text(). The net result? On the first iteration, the first text node displays the value of people:/people/person[1]/loginid, which is dsteinberg.
The above code is therefore, visually, the same as the following code.
<view name="table">
<simplelayout axis="y" spacing="10"/>
<view>
<simplelayout axis="x" spacing ="10"/>
<text>dsteinberg</text>
<text>Daniel</text>
<text>Steinberg</text>
<text>124</text>
</view>
<view>
<simplelayout axis="x" spacing ="10"/>
<text>chromatic</text>
<text>chromatic</text>
<text>NA</text>
<text>10089</text>
</view>
</view>
The fun part about this, however, is that you can change the dataset (and you can tell the view to automatically update itself).
The second way to manipulate datasets is via datapointers. A datapointer is, more or less, an object corresponding to a partial XPath expression (in much the same way that a view has a partial path). You define them using the datapointer tag, as in the following code snippet:
<datapointer xpath="people:/">
</datapointer>
In order to use datapointers, you usually define methods on them (since datapointers are instances of a class, you can add instance-specific methods, just like every other Laszlo object). Here, for example, is a very inefficient way to query the dataset defined above.
<datapointer name="database" xpath="people:/people" rerunxpath="true">
<method name="getNodeNumber" args="loginid">
<![CDATA[
for (i=0; i < this.getNodeCount(); i++) {
var path= "people:/people/person[" + (i+1) + "]";
setXPath(path);
if (this.xpathQuery("loginid/text()") == loginid) {
return i;
}
}
return -1;
]]>
</method>
<method name="isValidLoginID" args="loginid">
var nodeNumber = getNodeNumber(loginid);
return (-1 != nodeNumber );
</method>
<method name="getFirstName" args="loginid">
getNodeNumber(loginid);
return this.xpathQuery("firstname/text()");
</method>
<method name="getLastName" args="loginid">
getNodeNumber(loginid);
return this.xpathQuery("lastname/text()");
</method>
<method name="getNumberOfOranges" args="loginid">
getNodeNumber(loginid);
return this.xpathQuery("numberoforanges/text()");
</method>
<method name="getPassword" args="loginid">
getNodeNumber(loginid);
return this.xpathQuery("password/text()");
</method>
</datapointer>
This defines a datapointer and adds a few methods to it that are specific to the dataset it references. It's very inefficient, because it constantly resets the path of the data pointer in order to perform queries. (If we were worried about performance, or if the dataset were very large, we might wind up building secondary data structures and caching the results of commonly used XPath queries.)
Given our XML data set and our datapointer, we can now define our second screen and proceed to populate it. As usual, we start with the completed Flash. Here's the final version of the Oranges application.
With this version, if we enter "dsteinberg" with the password "secure," the screen changes entirely, to show us the number of oranges this particular user has eaten (and similarly for "chromatic" with password "reallysecure").
The code for this involves:
personalinformationwindow that displays information about a person. None of this is particularly hard at this point; the most complex thing involved is probably the JavaScript for handling logins. Here's that code:
<script>
<![CDATA[
function startApp() {
login_dialog.open();
}
function login(name, password) {
if (invalidLogin(name, password)) {
login_dialog.setMessage("Dude, That Was Both Lame <b> and </b> Bogus")
}
else {
successfulLogin(name);
}
}
function invalidLogin(loginid, password) {
if (database.isValidLoginId(loginid)) {
return password != database.getPassword(loginid);
}
return true;
}
function successfulLogin(loginid) {
login_dialog.close();
personal_information_window.display("ed", "zshau", "50");
var firstname = database.getFirstName(loginid);
var lastname = database.getLastName(loginid);
var numberoforanges = database.getNumberOfOranges(loginid);
personal_information_window.display(firstname, lastname, numberoforanges);
}
]]>
</script>
All this does is tie our original onclick, which calls login to our new datapointer and XML dataset.
At this point, I've shown you an application that validates a login and then displays a second page. While it is, admittedly, a very simple application, it does illustrate many of the basic principles of LZX programming. The glaring exception is communicating with the server. No matter how rich and interactive our Flash movie is, it needs to occasionally fetch data from the server, and occasionally store data to the server.
In Laszlo, this is also done via XML datasets. Instead of actually inserting the data into the dataset directly, you can specify a URL and have Laszlo fetch the data while the application is running. Similarly, you can alter a dataset, and then send the altered data to the server (getting back a dataset as a return value).
In both cases, communication with the server is done by sending XML payloads over HTTP. In essence, Laszlo replaces the standard web application model, fetch the entire page in HTML, submit forms, and then get the entire page back, with get and put XML datasets that just contain the information that we need.. When you add in the fact that the server compresses the XML, this results in a remarkable savings in bandwidth.
The Laszlo manual contains three entire chapters on how to do this, and effectively writing a data-driven application is far too large a topic to include in this article. So I'll just close with a quick warning: done naively, using XML datasets instead of HTTP might not improve the user's perceived performance very much. The problem is: the application is still communicating with the server. Suppose, for example, that the login panel above was implemented as follows:
This sends less data over the wire (especially after compression), but it still makes as many server calls as a standard web application. Which means that, while it reduces screen flicker (no ugly "white screens" while the page is refreshing), and reduces the total amount of data transmitted, it doesn't significantly reduce application latency very much, unless the time it takes for the web browser to render the screen is significant.
At this point, you've seen enough of Laszlo, and learned enough about both Laszlo and rich internet application frameworks in general, to start wondering whether it's an appropriate technology for you to use. There are really two questions involved, though, and it's best to separate them:
If you've read this far, and you're reading this section, then there are pretty good odds that you're intrigued by rich internet applications. But you're probably also a little bit skeptical. After all, these days, most new applications are built as web applications. For the most part, that means applications are built on the HTTP model: a stateless client that gets its entire user interface sent (as an HTML document) from the server each time the user does anything. And there have been numerous unsuccessful attempts to replace the HTML/HTTP-based Web over the years. (Speaking as someone who once owned a copy of Late Night VRML with Java, I can personally attest to this.)
To date, all of them have failed to gain significant traction because the HTML/HTTP-based Web has some very real advantages. Among them are:
In short, HTML and HTTP are commonly used because they're amazingly useful. In order to find a better application development model, you need to have an application where the Web doesn't work so well. Typically, this sort of application will have the following positive characteristics:
and the following negative ones:
Does your application meet these criteria? It's hard for me to say. I will say that web-based email and calendaring are pretty clearly in the sweet spot for rich internet applications, and that weblogs probably aren't.
Once you decide that a rich internet application framework is appropriate for your project, the question then becomes, "Should you use Laszlo?" I'm very fond of Laszlo. It has some impressive features in the current product (the 2.2 version) and some of the features coming in the 3.0 version are very nice (chief among them will be the ability to simply deploy the Flash, without needing a presentation server).
On the other hand, Laszlo also has some significant limitations. Many of them come from the fact that Laszlo currently only deploys via Flash. Flash is a nice platform, and using Flash makes Laszlo very easy to deploy, but it also means that the client side has some performance limitations. At the end of the day, the Flash virtual machine was designed for displaying movies. It's not about numerical calculations or large-scale data manipulations and I'm pretty sure Laszlo clients won't handle a XML dataset with 20,000 rows very well.
For similar reasons, Laszlo applications are single-threaded and tend to be event-driven. If your client application needs the ability to perform background computations, it's just not going to fit comfortably into the LZX application unless you can offload the computations to a server process.
The other major caution I'd give you is that LZX involves a new style of programming, and a fairly new programming language. The decision to use Laszlo is inherently a decision to be on the edge. There are very few libraries or frameworks for Laszlo at this point in time, not many people know how to use it, and the bookshelves are bare. If you're not an early adopter, this is not the time to adopt Laszlo.
All in all: should you adopt Laszlo? Maybe. What's very clear is that you should definitely download it and think hard about how rich internet applications will change the Web.
William Grosso is the vice president of engineering for Echopass.
|
|