Practical Consideration of Writing Applets
We have already discussed the applet-specific API in this appendix. However, most applets rely on a lot of API that isn't specific to applets. This section gives you hints about using the Java platform API, covering the areas that are affected by applets' close relationships with browsers.
Security Restrictions
One of the main goals of the Java platform is to make users feel secure running any applet in their browsers. To achieve this goal, browsers with the Java Virtual Machine started out con-servatively, restricting capabilities perhaps more than necessary. However, applets are getting more and more abilities, mainly by differentiating between trusted and untrusted applets.
How you mark an applet as trusted varies by browser. Some browsers that are written in the Java programming language implicitly and completely trust any applet loaded from a directory in the user's classpath. The reason is that an applet in the user's CLASSPATH has the same capabilities as the application that loads it.
Often, a browser uses a combination of signing and user-set preferences to indicate that classes with a certain signature are trusted. Another possibility is to have applets from certain URLs be trusted. Some browsers let the user specify certain files that any applet can read or write.
This section tells you about the security restrictions that browsers impose on untrusted applets and how they affect applet design. For more information on applet security, refer to this online FAQ: "Frequently Asked QuestionsApplet Security." [1]
[1] Available online at: http://java.sun.com/sfaq/index.html
Most browsers impose the following restrictions
Untrusted applets cannot load libraries or define native methods.
Untrusted applets can use only their own code and the API that the browser provides. At a minimum, each browser must provide access to the API defined in the java.* packages.
An untrusted applet cannot read or write files on the host executing it.
The JDK Applet Viewer permits some user-specified exceptions to this rule, but Netscape Navigator 4.0, for example, does not. A workaround for not being able to write files is to have the applet forward data to an application on the host from which the applet came. This application can write the data files on its own host.
An untrusted applet cannot make network connections except to the host from which it came.
For example, an untrusted applet can't read a file specified by a URL unless the file is on the applet's originating host. The reason is that it would be a security breach for applets delivered across firewalls to view files published within the firewall. The workaround for this restriction is to have the applet work with an application on the host from which it came. The application can make its own connections anywhere on the network.
An untrusted applet cannot start any program on the host executing it.
Again, an applet can work with a server-side application instead.
An untrusted applet cannot get many system properties.
See the section Getting System Properties (page 447) for more information.
Windows that an untrusted applet brings up look different from windows that an application brings up.
Windows brought up by untrusted applets have some warning text and either a colored bar or an image. This helps the user distinguish untrusted applet windows from those of trusted applications. Figure 119 shows a window brought up by a program that can run either as an applet or as an application. In Figure 119, the window appears as it does when the program is run as an application on the Windows 95 platform. In Figure 120, the window is shown as it appears when the program runs as an applet within the Windows 95 Applet Viewer. As you can see, the applet window has a warning.
Figure 119. A window brought up by an application.
Figure 120. A window brought up by an untrusted applet.
Creating a User Interface
Most applets have a GUI. This is a natural consequence of the fact that each applet appears within a browser window. Because the Applet class is a subclass of the AWT Panel class and thus participates in the AWT event and drawing model, creating an applet's GUI is just as easy as creating an application's GUIeasier, in fact, because the applet's window (the browser window) already exists. We've already seen a selection of the most popular AWT GUI components in the section AWT Components (page 419).
In addition to its graphical UI, an applet can use several other UI types, depending on the kind of information it needs to give or to get. Some applets play sounds, either to give the user feedback or to provide ambiance. Applets can get configuration information from the user through parameters that the applet defines. To give text information to the user, an applet can use its GUI, display a short status string (for text that's not crucial), or display to the standard output or standard error stream (for debugging purposes).
Following are a few issues that are particular to applet GUIs.
Applets appear in preexisting browser windows.
Unlike GUI-based applications, applets don't have to create a window in which to display themselves. They can create a window, but they often just display themselves within the browser window.
The applet background color might not match the page color.
Each browser defines a default background color for appletsusually white or light gray. HTML pages, however, can have other background colors and can use background patterns. If the applet designer and the page designer aren't careful, the applet's different background color can cause it to stick out on the page or cause noticeable flashing when the applet is drawn.
One solution to prevent this is to define an applet parameter that specifies the applet's background color. The Applet subclass can use Component's setBackground method to set its background to the user-specified color. Using the background color parameter, the page designer can choose an applet color that works well with the page colors. Parameters are described in the section Defining and Using Applet Parameters (page 433).
Each applet has a user-specified, predetermined size.
Because the tag requires that the applet's width and height be specified and because browsers don't usually allow applets to resize themselves, applets must make do with a fixed amount of space that might not be ideal. Some parts of the applet, such as text, might require different amounts of space on another platform. You can compensate by recommending that pages that include your applet specify a little more space than might be necessary. Then use flexible layouts that adapt well to extra space, such as the classes GridBagLayout and BorderLayout.Applets load images using the Applet getImage methods.
The Applet class provides a convenient getImage form that lets you specify a base URL as one argument, followed by a second argument that specifies the image file location, relative to the base URL. See the section the section Finding and Loading Data Files (page 424) for information on how to get base URLs.
Applet classes and data files are loaded over the network. This process can be slow.
Classes and data files that load slowly can cause noticeable delays when the user interacts with your program. For example, an applet might have a button that the user clicks to bring up a specialized window, to play a sound, or to draw graphics. If the user clicks the button before the applet has loaded the necessary classes or data, the user will notice a long delay while the browser fetches the necessary files.
Consider using background threads to preload classes and data that the user is likely to need. You also can archive related files in order to reduce transfer time. See the sections Threads in AWT Applets (page 449) and Combining an Applet's Files into a Single File (page 441) for information on how to implement threads and archives.
An Applet is a Panel.
Because Applet is a subclass of the Panel [1] class, applets can contain other Components, [2] just as any Panel can. Applets inherit Panel's default layout manager: FlowLayout. [3] As Panel objects (and thus Component objects), applets participate in the AWT drawing and event hierarchy.
[1] http://java.sun.com/products/jdk/1.1/api/java.awt.Panel.html
[2] http://java.sun.com/products/jdk/1.1/api/java.awt.Component.html
[3] http://java.sun.com/products/jdk/1.1/api/java.awt.FlowLayout.html
Displaying Diagnostics to the Standard Output and Standard Error Streams
Displaying diagnostics to the standard output or standard error stream can be an invaluable tool when debugging an applet. You'll also see messages at the standard output or standard error stream when an uncaught exception occurs in an applet. Even when an applet catches an exception, such as a security exception, the browser sometimes puts a message in the standard output or standard error stream before forwarding the exception.
Impurity Alert!
Displaying to the standard output and standard error streams has platform-dependent results and thus is not done in 100% Pure Java programs.
Where exactly the standard output and standard error streams are displayed varies, depending on how the browser is implemented, what platform it's running on, and (sometimes) how you launch the browser. For example, when you launch the Applet Viewer from a UNIX shell window, strings sent to the standard output and standard error appear in that shell window, unless you redirect the output. On the Win32 platform, when you launch the Applet Viewer from an MS-DOS prompt, the standard output and standard error streams go to the MS-DOS window. Netscape Navigator 4.0, on the other hand, displays applet standard output and standard error to the Java console.
Applets display to the standard output stream, using System.out.print(String ) and System.out.println(String ). To display to the standard error stream, specify System.err instead of System.out. Here's an example of displaying to the standard output:
//Where instance variables are declared: boolean DEBUG = true; . . . //Later, when we want to print some status: if (DEBUG) { System.out.println("Called someMethod(" + x + "," + y + ")"); }
Note
Displaying to the standard output and standard error streams is relatively slow. If you have a timing-related problem, printing messages to either of these streams might not be helpful.
Be sure to disable all debugging output before you release your applet. For example, if you have an applet that uses the code in the preceding example, you can turn off the applet's debugging output by setting DEBUG to false and recompiling the applet.
Getting System Properties
Applets find out about the current working environment by getting the values of system properties. System properties are key/value pairs that contain such information as the operating system under which the applet is running.
Untrusted applets can get some system properties but not all. This section lists the minimum set of system properties that applets can get, followed by a list of properties that untrusted applets can't get.
System Properties That Applets Can Get
Table 59 explains the system properties that even untrusted applets can get. It's possible that more properties will be added to this list.
Key |
Meaning |
---|---|
"file.separator" |
File separator (for example, "/") |
"line.separator" |
Line separator (for example, " ") |
"path.separator" |
Path separator (for example, ":") |
"java.class.version" |
Java class version number |
"java.vendor" |
Java vendor-specific string |
"java.vendor.url" |
Java vendor URL |
"java.version" |
Java version number |
"os.arch" |
Operating system architecture |
"os.name" |
Operating system name |
"os.version" |
Operating system version |
To get a system property, an applet uses the System class method getProperty:
String newline = System.getProperty("line.separator");
The following applet, called GetOpenProperties, gets the values of all the system properties listed in Table 59. [1] The GetOpenProperties applet's architecture is explained in the section Revisiting the GetOpenProperties Applet (page 453). [1] GetOpenProperties.java is included on the CD and is available online. See Code Samples (page 463). |
http://java.sun.com/docs/books/tutorial/applet/practicl/properties.html
Forbidden System Properties
For security reasons, untrusted applets can't get the system properties listed in Table 60. Applets might also be forbidden from getting other system properties.
Key |
Meaning |
---|---|
"java.class.path" |
Classpath |
"java.home" |
Installation directory |
"user.dir" |
User's current working directory |
"user.home" |
User's home directory |
"user.name" |
User's account name |
Threads in AWT Applets
Note
This section assumes that you're familiar with threads and so doesn't explain basic thread code. If you find yourself getting confused by the discussion or code in this section, read the chapter Threads: Doing Two or More Tasks at Once (page 269). If you're writing Swing applets, see section Threads in Swing Applets (page 459).
Every applet can run in multiple threads. Applet drawing methods (paint and update) are always called from a dedicated drawing and event-handling thread, the AWT event thread. Exactly which threads execute the milestone methodsinit, start, stop, and destroydepends on the browser that is running the applet. But no browser ever calls the milestone methods from the AWT event thread.
Browsers can vary in their approaches to using threads. One browser might allocate a thread for each applet on a page, using that thread for all calls to the applet's milestone methods. Another browser might allocate a thread group for each applet so that it's easy to find all the threads that belong to a particular applet. In any case, you're guaranteed that every thread created by any of an applet's milestone methods belongs to the same thread group.
Following are two PrintThread applets, one on top of the other: [1] [1] LamePrintThread.java and PrintThread.java are included on the CD and available online. See Code Samples (page 463). |
http://java.sun.com/docs/books/tutorial/applet/practical/threads.html
PrintThread is a modified version of the Simple applet, which prints the thread and thread group from which init, start, stop, destroy, and update are called. PrintThread calls repaint unnecessarily every once in a while so that you'll be able to see how its update method gets called. As usual, you need to look at the standard output to see the output for the methods, such as destroy, that are called during unloading.
So why would an applet need to create and to use its own threads? Imagine an applet that performs some time-consuming initialization in its init methodloading sound data, for example. The thread that invokes init cannot do anything else until init returns. In some browsers, this might mean that the browser cannot display the applet or anything after it until the applet has finished initializing itself. So if the applet is at the top of the page, nothing appears on the page until the applet has finished initializing itself.
Even in browsers that create a separate thread for each applet, it makes sense to put any time-consuming tasks into an applet-created thread so that the applet can perform other tasks while it waits for the time-consuming ones to be completed. When writing your threads, be careful that they don't rely on thread priorities for correct behavior. If you think you might need to specify thread priorities, see the section Understanding Thread Priority (page 286) in Chapter 8.
Rule of Thumb
An applet that performs a time-consuming task should create and use its own thread to perform that task.
Most applets implement the stop method, if necessary, to suspend any processing when the user leaves the applet's page. Sometimes, however, it's appropriate for an applet to continue executing. For example, if a user tells an applet to perform a complex calculation, the user might want the calculation to continue. (The user should generally be able to specify whether it should continue, though.)
Applets typically perform two kinds of time-consuming tasks: those they perform repeatedly and those they perform only once.
Using a Thread to Perform Repeated Tasks
An applet that performs the same task over and over again typically should have a thread with a while (or do-while) loop that performs the task. A typical example is an applet that performs timed animation, such as a movie player or a game. Animation applets need a thread that requests repaints at regular intervals. Another example is an applet that reads data supplied by a server-side application.
Applets typically create threads for repetitive tasks in the applet start method. Creating the thread there makes it easy for the applet to stop the thread when the user leaves the page. All you need to do is implement the stop method so that it tells the applet's thread to finish executing. When the user returns to the applet's page, the start method is called again. Then the applet can again create a thread to perform the repetitive task.
The AnimatorApplet puts its animation loop in a separate thread. Following is AnimatorApplet's implementation of the start and stop methods.
public void start() { ...//applet-specific code... //Start animating! if (animatorThread == null) { animatorThread = new Thread(this); } animatorThread.start(); } public void stop() { animatorThread = null; }
The this in new Thread(this) indicates that the applet provides the body of the thread. It does so by implementing the java.lang.Runnable interface, which requires the applet to provide a run method that forms the body of the thread.
Notice that nowhere in the AnimatorApplet class is the Thread stop method called. The reason is that calling the Thread stop method is like clubbing the thread over the head. It's a drastic way to get the thread to stop what it's doing. Instead, you can write the thread's run method in such a way that the thread exits gracefully when you tap it on the shoulder. This shoulder tap comes in the form of setting an instance variable.
In AnimatorApplet, this instance variable is called animatorThread. The start method sets it to refer to the newly created Thread object. When it needs to kill the thread, the applet sets animatorThread to null. This kills the thread not by making it be garbage-collectedit can't be garbage-collected while it's capable of runningbut because at the top of its loop, the thread checks animatorThread, continuing or exiting depending on the value of animatorThread.
Here's the relevant code:
public void run() { . . . Thread currentThread = Thread.currentThread(); while (currentThread == animatorThread) { ...//Display a frame of animation and then sleep. } }
If animatorThread refers to the same thread as the currently executing thread, the thread continues executing. However, if animatorThread is null, the thread exits. If animatorThread refers to another thread, a race condition has occurred. That is, start has been called so soon after stop (or this thread has taken such a long time in its loop) that start has created another thread before this thread reaches the top of its while loop. Whatever the cause of the race condition, this thread should exit.
For more information about and examples of stopping threads, see the section Stopping a Thread (page 285) in Chapter 8.
Using a Thread to Perform a One-Time Task
If your applet needs to perform a task that can take a while, consider performing it in a thread. For example, anything that requires making a network connection should generally be done in a background thread. Anything with deliberate pauses should always be done in a background thread.
Fortunately, the AWT Image class loads GIF and JPEG images in the background, using threads that you don't need to worry about. Sound loading, unfortunately, is not guaranteed to be done in the background. In the JDK 1.1 implementation, the Applet getAudioClip methods don't return until they have loaded all the audio data. As a result, you might want to create one or more threads to load sounds.
Revisiting the GetOpenProperties Applet
The section Getting System Properties (page 447) has an applet that uses a thread to initialize its display. When the applet is initialized, it sets up its user interface, which consists of two columns of labels. The applet waits 3 seconds to give the user time to see the initial text in the labels. Then, one by one, the applet changes the text displayed by the labels in the right column. The applet's appearance doesn't change after that.
Because the applet's appearance doesn't change after the applet is initialized, it makes sense to put the applet's functionality in its init method. However, it's not good for the init method to execute for a long time. For this reason, the init method spins off a low-priority thread that performs the time-consuming part of the initialization.
Another way to implement the applet would be to have the start method start the thread. This would make it possible to stop the applet if the user quickly left the page. If the user returned to the page, the applet could start initialization all over again or could pick up where it left off. However, in this applet, using init should work just fine, because initialization shouldn't take more than a few seconds and because it's performed in a thread with very low priority that frequently yields the processor.
Here's the code that implements the thread:
public class GetOpenProperties extends Applet
implements Runnable {
. . .
public void init() {
...//Create and add a bunch of Labels...
new Thread(this, "Loading System Properties").start();
}
/*
* This method runs in a separate thread, loading
* properties one by one.
*/
public void run() {
. . .
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
//Pause to let the reader see the default strings.
pause(3000);
for (int i = 0; i < numProperties; i++) {
//Pause for dramatic effect.
pause(250);
...//Look up a value; make a label show it...
}
}
}
synchronized void pause(int millis) {
try {
wait(millis);
} catch (InterruptedException e) { }
}
}
Категории