Monday, May 25, 2009

Lazy Loading of JavaFX Applets

Web pages that contain large applets or many small applets can load slowly. This annoyance is one of the reasons that applets have historically been unpopular. Sun has been pushing to revive the applet by improving its reliability and encouraging its use for JavaFX deployment. Despite these improvements, JavaFX applets can produce unacceptable page load times. Java Web Start is an alternative deployment mechanism and is currently regarded as a more robust solution. Web Start avoids browser compatibility issues by running apps in a separate window from the browser, thus not affecting page load time. But there are times when it is preferable to run an application from directly within a web page.

I will show how to keep an applet within the browser window and delay applet loading until the user performs some input action, like pressing a button. The applet is said to be loaded lazily, rather than loaded eagerly at page load time. Solutions for both JavaFX and traditional Java applets will be presented.


JavaFX Applets
One way to insert a JavaFX applet into a web page is to include two JavaScripts. The first links to Sun's dtfx.js JavaScript file:
<script src="http://dl.javafx.com/1.1/dtfx.js" type="text/javascript"></script>
The second script passes the applet's launch parameters to the “javafx” function defined in the dftx.js file. Here is an example:
<script type="text/javascript">
  javafx({
    archive: "http://jfreechartscaler.appspot.com/fx/jfreechart-scalable-scrollable.jar",
    draggable: true,
    width: 466,
    height: 288,
    code: "jfreechartscalablescrollable.Main",
    name: "jfreechart-scalable-scrollable"
  });
</script>
The “javafx” function above reads in the applet launch parameters and embeds the <APPLET> tag into the HTML of the web page. The applet tag is customized for best appearance on various operating systems and web browsers.

Rather than calling the “javafx” function to insert the applet tag into the web page, we will instead call the “javafxString” function and save the generated HTML into a string. The entire operation will be placed in a JavaScript function called startApplet(). So the JavaScript above will be replaced with the following script:
<script type="text/javascript">
  <!--
  function startApplet() {
    fxstring = javafxString({
      archive: "http://jfreechartscaler.appspot.com/fx/jfreechart-scalable-scrollable.jar",
      draggable: true,
      width: 466,
      height: 288,
      code: "jfreechartscalablescrollable.Main",
      name: "jfreechart-scalable-scrollable"
    });
    document.getElementById('appletdiv').innerHTML=fxstring;
  }
  //-->
</script>
The function refers to a <div> named ‘appletdiv’ that will contain the applet. When the function is executed, the inner HTML of the <div> will be replaced with the <APPLET> tag returned from the javafxString function. The function is called when the user clicks on an image that is initially placed in the <div> when the page loads:
<div id="appletdiv" style="text-align: center;">
  <input type="image" src="http://patrickwebster.synthasite.com/resources/scroller-applet.png" alt="Click here to start applet." onmousedown="startApplet()" />
</div>
I chose to fill the div with an image that is the same size as the applet. This prevents the layout of the page from changing when the applet loads, providing a seamless transition. If you are not concerned with minimizing the intrusiveness of loading the applet, you may initiate applet loading with a button press using this more simple div:
<div id="appletdiv" style="text-align: center;">
  <input type="button" value="Click here to start applet" onclick="startApplet()" />
</div>
If all this applet stuff is new to you, I recommend that you first try lazy loading of applets using the simple button press. After everything is working correctly, you may replace the button with a custom image if desired.

A live example of using a clickable image to load a JavaFX applet can be found here. The applet on that page can be embedded into almost any web page by pasting this snippet into the HTML code:
<script src="http://dl.javafx.com/1.1/dtfx.js" type="text/javascript"></script>
<script type="text/javascript">
  <!--
  function startApplet() {
    fxstring = javafxString(
      {
      archive: "http://jfreechartscaler.appspot.com/fx/jfreechart-scalable-scrollable.jar",
      draggable: true,
      width: 466,
      height: 288,
      code: "jfreechartscalablescrollable.Main",
      name: "jfreechart-scalable-scrollable"
      }
    );
    document.getElementById('appletdiv').innerHTML=fxstring;
  }
  //-->
</script>

<div id="appletdiv" style="text-align: center;">
  <input type="image" src="http://patrickwebster.synthasite.com/resources/scroller-applet.png" alt="Click here to start applet." onmousedown="startApplet()" />
</div>


Java Applets
Lazy loading of standard Java applets is easier than with JavaFX applets because the applet code is known without having to query a server-side JavaScript function. The concept of using a div as a placeholder is the same as in the JavaFX applet case. An example of a traditional eager-loading applet is here:
<applet codebase="http://apppspot.appspot.com/java/lib" archive="jfreechart-magnifier-applet.jar,jfreechart-1.0.12.jar,jcommon-1.0.15.jar,jxlayer.jar" code="JFreeChartMagnifierApplet" alt="Dude, like you totally need Java SE 6 or later to run this applet." height="288" width="466">
 <param name="draggable" value="true">
 <param name="java_arguments" value="-Djnlp.packEnabled=true">
</applet>
To transform this eager applet into a lazy one, we will again write a JavaScript function to build the <applet> tag into string form. But we will concatenate it manually because it is already known. Building the <applet> tag above into a string is achieved with this JavaScript:
<script type="text/javascript">
  <!--
  function startApplet() {
    appletsource='<applet code="JFreeChartMagnifierApplet" codebase="http://apppspot.appspot.com/java/lib" archive="jfreechart-magnifier-applet.jar,jfreechart-1.0.12.jar,jcommon-1.0.15.jar,jxlayer.jar" alt="Dude, like you totally need Java SE 6 or later to run this applet." height="288" width="466">\n'; 
    appletsource+='<param name="draggable" value="true">\n';
    appletsource+='<param name="java_arguments" value="-Djnlp.packEnabled=true">\n'; 
    appletsource+='</applet>\n'; 
    document.getElementById('appletdiv').innerHTML=appletsource;
  }
  //-->
</script>
The string appletsource is set equal to the inner HTML of the div named ‘appletdiv’. The div is nearly identical to the JavaFX case presented earlier:
<div id="appletdiv" style="text-align: center;">
  <input type="image" src="http://patrickwebster.synthasite.com/resources/magnifier-applet.png" alt="Click here to start applet." onmousedown="startApplet()" />
</div>
To view the live example of this lazy loading java applet, click here.


Conclusion
To provide the best user experience, page load times should be minimized. Lazy loading of applets ensures that applet load time does not contribute to initial page load time. The user only incurs the penalty of waiting for a potentially slow-loading applet after choosing to do so. The user has the right to not load the applet, and in doing so is charged a minimal cost. The only cost is the initial page load time, which can be quite small compared to the time to load a large applet. By amortizing the page load and applet load times, the perceived wait time is decreased, resulting in an overall improved experience. It is polite to put the user in control. It is rude to force the user to wait a long time for a page to load without giving any warning. So please, be lazy!