Monday, August 31, 2009

The invisible applet

How about deploying a Java applet that you can't see?

It's no secret that Java has not been successful as a client-side platform. We often use HTML/Ajax or Flash/Flex on the browser and Java on the server. Maintaining 2 distinct platforms has a major disadvantage: it's hard to share code.


Imagine this scenario: you have built a beautiful UI for the browser, and like any respectable shop you maintain your business logic in Java on the server. Suppose you design a screen where your user can do what-if analysis by fiddling with sliders and watching pretty graphs and numbers change in real time. Unfortunately, the code that does all that complex computation is in Java on your server. Your options:

  • Write a parallel ActionScript codebase that duplicates the Java logic. You have the burden of keeping both codebases correct and in sync, and there could be subtle inconsistencies in the resulting logic.
  • Keep the logic on the server, and execute the computation there. Every time the user jiggles a slider, the Flex app bundles all the relevant data and transmits it to the server. The server in turn does the computation and sends back the results. 
My reaction to these alternatives is "yuck". I would have to pay a cost in either development burden or network traffic and performance. This would be one example where a Java "engine" on the browser might be exactly what you need, even if it does no UI duty.

Implementing an invisible Java applet to provide services to your UI code via JavaScript is surprisingly easy. Your applet tag needs a MAYSCRIPT attribute to enable the Java-to-JavaScript interface:

<applet ...="" height="0" id="myapplet" mayscript="" width="0">
...
</applet>


Note that this is a 1x1 box. If the applet is zero size or wrapped in a hidden div, the applet won't start. Once your applet is running you can call public methods in your Applet class directly from JavaScript:

document.getElementById('myapplet').myJavaMethod();

Conversely, you can call your JavaScript methods from within your applet. Since JavaScript is single-threaded, for longer operations you might want to use an asynchronous call/callback pattern where the Java code would spawn a separate thread that returns the result by way of a callback into your JavaScript code. You can do it like this:

JSObject.getWindow(applet).call("myJsMethod", new Object[] { arg });

One odd behavior I found when calling Java methods from JavaScript is that even when the applet is signed, the Java code executes in a thread with reduced privileges. Jumping onto the Swing event thread will gain you the signed privileges, but now you are tying up the event thread. If you want privileged, multithreaded execution, you could do something like this (looks bizarre, I know):

SwingUtilities.invokeLater(new Runnable() {
   public void run() {
      new Thread() {
         public void run() {
            doWhatIReallyWantToDo(); 
         } 
      }.start(); 
   } 
});

That seems easy enough, but there still aren't many situations that justify using Java on the client side. The dominance and ubiquity of Ajax and Flash on the browser means you need a really good reason to burden your users with the Java runtime. Deployment remains Java's big weakness on the client. Some possible reasons:
  • To share logic on both client and server.
  • To use functionality from some complex third-party library on the client side. 
  • Get multithreaded, fast execution.
  • Need for extensive filesystem access.
  • Some combination of the above. 

5 comments:

  1. Chris, your article was very interesting. I have another use case for which I have been pondering about using an invisible applet: providing feedback for asynchronous operations. My idea is to have an applet listening on a JMS queue / topic, whose only purpose is to notify the web app about "events" (whatever these might be). As soon as a message is received on the queue, the applet will simply pass it onto Javascript so that some action can be taken.

    What do you think about this idea? Do you see any caveats? Can you point me to any resource that could help me refine my ruminations?

    Thanks a lot and best regards.

    ReplyDelete
  2. Thanks for your contribution, Gonzo. I don't have anything to add to your idea, since honestly this is not something that I have thought much about. JMS communications is normally a server-side operation, so I have not really encountered a use case for it on the client side. Nevertheless, the idea of an applet that listens for client-side events with which to notify the server could be useful in the abstract.

    ReplyDelete
  3. At least in Google Chrome the applet does not load with width=0/height=0.
    So your method won't work!

    ReplyDelete
    Replies
    1. Thanks for the observation, Nikolas. We currently load the applet with a size of 1px by 1px, which is still practically invisible but works in all major browsers.

      Delete
    2. I discovered that you can complete hide a Java Applet if you specify a style for the applet element. The width and height attributes are both 1px, but the style sets the height and width to 0px. You can specify the style through CSS or a style attribute.

      For example:
      <applet id="myHiddenApplet" width="1px" height ="1px" style="width: 0px; height: 0px;" etc="..."> ...

      It only took me 16 hours of experimentation and scouring the internet for articles like yours. Thanks for the informative post, Chris.

      Delete