Showing posts with label browser. Show all posts
Showing posts with label browser. Show all posts

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!


Sunday, February 1, 2009

Dragging Applets Out of the Browser in Xubuntu (Intrepid Ibex)

Java SE 6 Update 10 provides the ability to drag applets out of the browser. The two steps necessary to enable this functionality in Xubuntu 8.10 (Intrepid Ibex) are:
  1. Change the window-dragging keyboard shortcut in your window manager.
  2. Update the outdated java plugin links.
The second step is only necessary before upgrading to Java SE 6 Update 11, which has already been released as an automatic update in Windows, but has yet to appear automatically in the Ubuntu flavors of linux. (Actually, Update 12 has already been released.) The first step will be explained below using the Xfce desktop environment in Xubuntu 8.10 (Intrepid Ibex). The remaining step applies to other Ubuntu flavors as well (Kubuntu, Edubuntu, etc.).


Step 1: Changing the Window Dragging Key
In Xfce, bring up the Settings -> Settings Manager. Click on “Window Manager Tweaks” and go to the “Accessibility” tab. Change the “Key used to grab and move windows” to anything but Alt. In the image below, Super is chosen:

(For instructions on how to do this in GNOME, see Cay Horstmann's blog.)
If Java 6 Update 11 or later is installed, applying the new key shortcut above should enable applet dragging and the remaining steps should not be necessary.


Step 2: Update the Java Plugin Link
I will present two methods for updating the links to the java plugin. The first uses the graphical user interface tool G Alternatives, and the second uses the command-line tool update-alternatives.

GUI Method
First, install G Alternatives using the following command:
sudo apt-get install galternatives
Launch the program with root access:
sudo galternatives &
Scroll down to the bottom of the Alternatives list on the left side and select “xulrunner-1.9-javaplugin.so” as shown here:


Select “auto” in the Status chooser. The Options table shows only one choice with priority 63. This is the location of the old plugin. To add the updated plugin, click “Add” and paste this path in the dialog:
/usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so
Only valid file paths are allowed. So don't worry about typing the wrong value and screwing up your system. If you make a mistake in the library path, you can browse to the correct location. Enter a priority larger than the priority of the old plugin (or any other plugins in the Options table). I chose 77 as shown here:


Clicking OK should produce something like this:


Notice that the new plugin is automatically chosen in the Choice column. This is because the auto status will automatically select the alternative with the highest priority. At this point, the system is updated. Without closing G Alternatives, launch a new Firefox browser and test if applet dragging is enabled. For my freshly installed Xubuntu system, this was all that was necessary for Firefox to start using the new plugin.

If applet dragging still does not work, close Firefox and return to G Alternatives. Scroll up in the list of alternatives to “mozilla-javaplugin.so,” as shown here:


Select “auto” status if is it not already selected. Add a new option with the same path and priority as the XULRunner case above. Choose a priority larger than any other options in the table. In my case, I again chose 77 because it is larger than the highest-priority alternative option (63).


Without closing G Alternatives, test Firefox again. On my Kubuntu system, changing both the XULRunner and Mozilla Java plugins was sufficient to correct the plugin behavior in Firefox.

Once you have applet dragging working properly, you may quit. But a cleaner solution is to update all links to the old Java plugin. Sun recommends removing all links to the old java plugin. The list of remaining plugins that should be updated is:
firefox-javaplugin.so
iceape-javaplugin.so
iceweasel-javaplugin.so
midbrowser-javaplugin.so
xulrunner-javaplugin.so
The procedure to update these libraries is identical to the previous examples. Select each one and add a new path to the updated plugin with top priority and auto status.

Command-Line Method
The following achieves the same result as the GUI method above. The command update-alternatives will be used to make Firefox see the correct XULRunner library. To display the current library pointed to by the XULRunner link, execute the following:
sudo update-alternatives --display xulrunner-1.9-javaplugin.so
The output should look something like this:
xulrunner-1.9-javaplugin.so - status is auto.
link currently points to /usr/lib/jvm/java-6-sun/jre/plugin/i386/ns7/libjavaplugin_oji.so
/usr/lib/jvm/java-6-sun/jre/plugin/i386/ns7/libjavaplugin_oji.so - priority 63
Current `best' version is /usr/lib/jvm/java-6-sun/jre/plugin/i386/ns7/libjavaplugin_oji.so.
This says that the library xulrunner-1.9-javaplugin.so points to the old plugin libjavaplugin_oji.so with auto-selected priority 63. If status is manual, set it to auto with the following command:
sudo update-alternatives --auto xulrunner-1.9-javaplugin.so
We want to install a link to the new plugin with a priority greater than 63. The following will achieve this:
sudo update-alternatives --install /usr/lib/xulrunner-1.9.0.5/plugins/libjavaplugin.so xulrunner-1.9-javaplugin.so /usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so 77
(You may have a slightly different version, like 1.9.0.4. If so, edit the command appropriately.)

Checking the display with
sudo update-alternatives --display xulrunner-1.9-javaplugin.so
produces:
xulrunner-1.9-javaplugin.so - status is auto.
link currently points to /usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so
/usr/lib/jvm/java-6-sun/jre/plugin/i386/ns7/libjavaplugin_oji.so - priority 63
/usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so - priority 77
Current `best' version is /usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so.
So now the link points to the new plugin with priority 77, making it the best and current link. Launch a new Firefox and test if applet dragging works. If not, update the mozilla-javaplugin.so library in a similar manner. First, execute the following:
sudo update-alternatives --display mozilla-javaplugin.so
The output should look something like this:
mozilla-javaplugin.so - status is auto.
link currently points to /usr/lib/jvm/java-6-sun/jre/plugin/i386/ns7/libjavaplugin_oji.so
/usr/lib/jvm/java-6-sun/jre/plugin/i386/ns7/libjavaplugin_oji.so - priority 63
Current `best' version is /usr/lib/jvm/java-6-sun/jre/plugin/i386/ns7/libjavaplugin_oji.so.
This is nearly identical to the XULRunner case above. Set the status to auto if necessary:
sudo update-alternatives --auto mozilla-javaplugin.so
Install the new alternative:
sudo update-alternatives --install /usr/lib/mozilla/plugins/libjavaplugin.so mozilla-javaplugin.so /usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so 77
That should fix applet dragging. But for the cleanest solution, Sun recommends updating the following libraries as follows:
sudo update-alternatives --install /usr/lib/firefox/plugins/libjavaplugin.so firefox-javaplugin.so /usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so 77
sudo update-alternatives --install /usr/lib/iceape/plugins/libjavaplugin.so iceape-javaplugin.so /usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so 77
sudo update-alternatives --install /usr/lib/iceweasel/plugins/libjavaplugin.so iceweasel-javaplugin.so /usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so 77
sudo update-alternatives --install /usr/lib/midbrowser/plugins/libjavaplugin.so midbrowser-javaplugin.so /usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so 77
sudo update-alternatives --install /usr/lib/xulrunner/plugins/libjavaplugin.so xulrunner-javaplugin.so /usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so 77
Make sure they all have auto status and 77 is the highest priority.


Last Resort
If updating /etc/alternatives/ still does not fix applet dragging in Firefox 3, you may try fiddling with links directly, but this may cause problems later when upgrading.

There are several places where Firefox 3 looks for Java plugins. One place is /usr/lib/firefox-addons/plugins/. Place a link to the updated plugin in this directory using the following command:
sudo ln -s /usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so /usr/lib/firefox-addons/plugins/sir_mix_a_lot
You may question the name of the above link. I chose this name to make the point that the name does not matter. Firefox will follow the link and load the plugin that it points to. The important thing to learn is that because names do not matter, RENAMING A PLUGIN WILL NOT DISABLE IT. If you want to remove a plugin, MOVE IT OR LOSE IT! You should probably rename the link to something more traditional, like “libnpjp2.so.”

Note that the directory /usr/lib/firefox/plugins/ is NOT the right place to fix applet dragging in Firefox 3. You can modify that directory all you want and it won't help. It may be a good idea to remove the old library link named “libjavaplugin.so” which points to /usr/lib/jvm/java-6-sun/jre/plugin/i386/ns7/libjavaplugin_oji.so which is the old plugin. Sun recommends removing all links to the old library. But removing this link is not necessary to enable applet dragging.

Another place to update the Firefox plugin is in the user's home directory. If the directory ~/.mozilla/plugins/ does not exist (it didn't exist on my machine), create it and execute the following command:
ln -s /usr/lib/jvm/java-6-sun/jre/lib/i386/libnpjp2.so ~/.mozilla/plugins/baby_got_back
Once again, the name of the link does not matter. You may choose the name “libnpjp2.so” if you like.

So that is what worked for me. Your mileage may vary.