Applet Developer's Guide > Java Plug-in and Applet Architecture > Best Practices For Applet Development
Contents
Applets have a complex runtime environment and several issues need to be considered to ensure that applets run predictably on various browsers and versions of Java Plug-ins. This document describes the best practices for applet development and deployment .
Values stored in the applet could persist between
invocations, if the memory acquired by the class loader cache isn't
needed for other purposes. But you can't depend on that behavior.
In general, applets should be stateless. If persistent storage is
needed, use browser cookies.
start
method as quickly as possibleIf start
doesn't terminate, stop
can't
be called. That's important, because garbage collection occurs
after the destroy
method terminates. If the applet
lingers in the start
method, then the applet may not
be torn down properly. (It will be torn down, and garbage
collection will occur, but resources acquired during applet
initialization may not be properly released.)
For a computationally-intensive app like the Clock demo, the trick is to implement the
Runnable
interface, do the heavy lifting in the
run
method it requires, and launch a separate thread
to do the actual work, as shown in this abstract from the
source code:
public class Clock extends Applet implements Runnable { private volatile Thread timer; // The thread that displays the clock ... public void start() { timer = new Thread(this); // Create the thread and start it timer.start(); } public void stop() { timer = null; // Release the thread resource } public void run() { Thread me = Thread.currentThread(); while (timer == me) { try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { } repaint(); } } ...
Here, the action is to pause the Clock instance that's running
in the new thread (hence the need to store a pointer to it.)
Applets that don't need to do all that can simply put their
processing code into the run
method.
For an interactive app like the spreadsheet demo, that goal is easily achieved, since the majority of time is spent in the event-processing thread, as shown in this abstract from the source code:
public class SpreadSheet extends Applet implements MouseListener, KeyListener // Event processing { public void init() { ... addMouseListener(this); addKeyListener(this); } ... public void start() { isStopped = false; } public void stop() { isStopped = true; } public void keyTyped(KeyEvent e) { // Invoked when an event occurs ... } public void mousePressed(MouseEvent e) { ... } ... class CellUpdater extends Thread { ... public void run() { ... if (!target.app.isStopped && !target.paused) { target.app.repaint(); }
In this particular implementation, each cell has it's own update
thread. Updating is paused when a cell is selected, and only stops
completely when the applet terminates. The important point is that
the applet's start
and stop methods only keep
track of applet state. Most of the time is spent in the underlying
event-processing thread, waiting for the user's keystroke or
mouseclick. The applet's real work then occurs in response to one
of those events.
An ID defines the namespace, and ensures proper naming at all levels of the data hierarchy:
<applet id="myApplet" name="myApplet" ...
Truly stateless applets don't depend on values previously stored in static variables. Since you can't depend on those values to be retained, it's a good idea to create stateless applets. But the user can still store state. It's just that store the data where it belongs--with the user, rather than in the applet code.
To implement a stateless app, access browser cookies or launch with JNLP to use Java Web Start muffins, so the applet gets it's initial settings from persistent storage, rather than using values stored in the program.
To ensure that your applet is stateless, use the classloader_cache attribute to disable the cache. For example:
<applet ... classloader_cache="false"
That setting ensures that the applet is stateless, and that it is truly decoupled from the remainder of the system and from other instances of the applet. It also ensures that the applet runs the exact same way every time, since it won't be affected by state that has been inadvertently retained.
Note:
In the rare case that you expect your users to re-run the same
applet repeatedly, you might consider leaving the cache enabled for
production for the sake of performance, but disable it during
development to ensure quality.
To prevent thread contention, don't update the Swing or AWT GUI from javascript directly. Instead, set up a proxy that forwards the request to the appropriate dispatch thread:
javax.swing.SwingUtilities.invokeLater(Runnable)
java.awt.EventQueue.invokeLater(Runnable)
Minimize the time taken to call from Java into javascript, and from javascript into Java. Don't do long-running work in such calls, or you risk a deadlock. To keep the calls short:
When calling into Java, don't make calls back into javascript if you can possibly help it, and vice versa. In that original plugin, doing so used to lock up the browser. That shouldn't happen anymore, but round-trip calls do introduce significant delays in processing time.
When a javascript function needs the applet to do something, the
best practice is to return a value that tells the applet to do it,
and then call back with the results, or else use
invokeLater
to minimize traffic tie ups.
The Java platform has powerful multi-threading capabilities that
you can take advantage of with classes like Thread, Swing
invokeLater
, and AWT
invokeLater
, as well as the new ThreadPoolExecutor class
that maintains a background thread pool.