Practical Considerations When Writing Applets






Practical Considerations When Writing Applets

The first two sections in this chapter discussed all of the applet-specific API. However, most applets rely on a lot of API that isn't specific to applets. This section gives you hints about using the Java API, covering the areas that are affected by applets' close relationships with browsers.

Security Restrictions

One of the main goals of the Java environment is to make browser users feel secure running any applet. To achieve this goal, we've started out conservatively, restricting capabilities perhaps more than necessary. As time passes, applets will probably get more and more abilities.

This section tells you about the current applet security restrictions, from the point of view of how they affect applet design. For more information on applet security, you should refer Frequently Asked QuestionsApplet Security.[30]

[30] http://java.sun.com/sfaq/

Each applet viewer has a SecurityManager object that checks for applet security violations. When a SecurityManager detects a violation, it creates and throws a SecurityException object. Generally, the SecurityException constructor prints a warning message to the standard output. An applet can catch SecurityExceptions and react appropriately, such as by reassuring the user and by resorting to a "safer" (but less ideal) way of accomplishing the task.

Some applet viewers swallow some SecurityExceptions, so that the applet never gets the SecurityException. For example, the JDK Applet Viewer's implementation of the AppletContext getApplet and getApplets methods simply catches and ignores any SecurityExceptions. The user can see an error message in the standard output, but at least the applet gets a valid result from the methods. This makes some sense, since getApplets should be able to return any valid applets it finds, even if it encounters invalid ones. (The Applet Viewer considers an applet valid if it's loaded from the same host as the applet that's calling getApplets.)

To learn about security managers and the kinds of security violations they can check for, see The Security Manager section (page 455).

Existing applet viewers (including Web browsers) impose the following restrictions:

Applets cannot load libraries or define native methods. Applets can use only their own Java code and the Java API the applet viewer provides. At a minimum, each applet viewer must provide access to the API defined in the java.* packages.

An applet cannot ordinarily read or write files on the host that is executing it. The JDK Applet Viewer actually permits some user-specified exceptions to this rule, but older browsers generally do not. Applets in any applet viewer can read files specified with full URLs, instead of by a filename. A workaround for not being able to write files is to have the applet forward data to an application on the host the applet came from. This application can write the data files on its own host. See the Working with a Server-Side Application section (page 589) for more examples.

An applet cannot make network connections except to the host that it came from. The workaround for this restriction is to have the applet work with an application on the host it came from. The application can make its own connections anywhere on the network. See the Using a Server to Work Around Security Restrictions section (page 591) for an example.

An applet cannot start any program on the host that is executing it. Again, an applet can work with a server-side application instead.

An applet cannot read certain system properties. See the Getting System Properties section (page 583) for more information.

Windows that an applet brings up look different than windows that an application brings up. You can identify the Applet window by the name "Java Applet Window," which is displayed at the bottom of the window. The application window would not have any name at its bottom. This helps the user distinguish applet windows from those of trusted applications.

Figures 18.15 and 18.16 show a window brought up by a program that can run either as an applet or as an application. Figure shows what the window looks like when the program is run as an application on the Microsoft Windows platform. Figure shows the window when the program runs as an applet on the Windows platform within the Mozilla browser.

15. A program running as an application.

16. Same program running as an applet.

As you can see, the applet window has a label informing the user that it is running as an applet.

Creating a User Interface

Most applets have a graphical user interface (GUI). This is a natural consequence of the fact that each applet appears within a browser window. Because the JApplet class is a subclass of the Applet class, which 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 GUI. It's easier, actually, since the applet's window (the browser window) already exists.

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 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).

For information about sound, parameters, and status strings, see the Taking Advantage of the Applet API section (page 559).

Creating a GUI

This section discusses the few issues that are particular to applet GUIs. Some of the information in this section might not make sense until you've read the Creating a GUI with JFC/Swing and, in particular, the How to Make Applets section.[31] That trail discusses all the GUI concepts referred to in this section:

[31] tutorial/uiswing/components/applet.html

Applets appear in preexisting browser windows. This has two implications. First, unlike GUI-based applications, applets don't have to create a window in which to display themselves. They can, if they have a good reason, but they often just display themselves within the browser window. Second, depending on the browser implementation, your applet's components might not be shown unless your applet calls validate after adding components to itself. Fortunately, calling validate can't hurt.

The applet background color might not match the page color. By default, applets have a white background color. HTML pages, however, can have other background colors and can use background patterns. If the applet designer and 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 is to define an applet parameter that specifies the applet's background color. The JApplet class can use JComponent's setBackground method to set the applet's background to the color specified in the Web page. Using the background color parameter, the page designer can choose an applet color that works well with the page colors. You've learned about parameters in the Defining and Using Applet Parameters section (page 570).

Each applet has a user-specified, predetermined size. The <APPLET> tag requires that the applet's width and height be specified. The Web designer can set an applet's size by pixels or by indicating a percentage of the browser window. Note that even if the amount of space is ideal for one platform, the platform-specific parts of the applet (such as buttons) might require a different amount 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, and by using flexible layouts, such as the GridBagLayout and BorderLayout classes, that adapt well to extra space.

Applets load images using the Applet getImage methods. The Applet class provides a convenient form of getImage 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. The Applet getCodeBase and geTDocumentBase methods provide the base URLs that most applets use. Images that an applet always needs, or needs to rely on as a backup, are usually specified relative to where the applet's code was loaded from (the code base). Images that are specified by the applet user (often with parameters in the HTML file) are usually relative to the page that includes the applet (the document base).

Applet classes (and often the data files they use) are loaded over the network, which can be slow. Applets can do several things to decrease the perceived startup time. The Applet subclass can be a small one that immediately displays a status message. If some of the applet's classes or data aren't used right away, the applet can preload the classes or data in a background thread.

For example, the AppletButton[32] class start method launches a thread that gets the Class[33] object for the window the button brings up. The applet's main purpose in doing so is to make sure the class name that the user specified is valid. An added benefit is that getting the Class object forces the class file to be loaded before the class is instantiated. When the user requests that the window be created, the applet instantiates the window class much quicker than if the applet still had to load the window class file.

[32] tutorial/uiswing/layout/example-swing/AppletButton.java

[33] docs/api/java/lang/Class.html

Displaying Diagnostics to the Standard Output and Error Streams

Displaying diagnostics to the standard output can be an invaluable tool when you're debugging an applet. Another time you'll see messages at the standard output is when an uncaught exception occurs in an applet. Applets also have the option of using the standard error stream.

Where exactly the standard output and error are displayed varies, depending on how the applet's viewer is implemented, what platform it's running on, and (sometimes) how you launch the browser or applet viewer. When you launch the Applet Viewer from a UNIX shell window, for example, strings displayed to the standard output and error appear in that shell window, unless you redirect the output. When you launch the Applet Viewer from an X Windows menu, the standard output and error go to the console window.

Applets display to the standard output stream using System.out.print(String) and System.out.println(String). Displaying to the standard error stream is similar; just 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 error streams is relatively slow. If you have a timing-related problem, printing messages to either of these streams might not be helpful.


You should be sure to disable all debugging output before you release your applet.

Getting System Properties

To find out about the current working environment, applets can read system properties. System properties are key/value pairs that contain information such as the operating system that the applet is running under. System properties are covered in detail in the Properties section (page 443).

Applets can read some, but not all, system properties.

System Properties That Applets Can Read

Applets can read the system properties listed in Figure.

Valid System Properties

Key

Meaning

"file.separator"

File 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

"line.separator"

Line separator

"os.arch"

Operating system architecture

"os.name"

Operating system name

"path.separator"

Path separator (for example, ":")


To read a system property from within an applet, the applet uses the System class method getProperty. For example:

String newline = System.getProperty("line.separator");

Run an applet[34] which reads all of the properties available to all applets (Figure).

[34] tutorial/deployment/applet/examples/properties.jar

17. A screenshot of the GetOpenProperties applet.


You can find the source code in GetOpenProperties.java.[35]

[35] tutorial/deployment/applet/examples/GetOpenProperties.java

Forbidden System Properties

For security reasons, no existing browsers or applet viewers let applets read the system properties listed in Figure.

Forbidden System Properties

Key

Meaning

"java.class.path"

Java classpath

"java.home"

Java installation directory

"user.dir"

User's current working directory

"user.home"

User home directory

"user.name"

User account name


Threads in Applets

Note

This section assumes that you know what a thread is. If you don't, please read the Processes and Threads section (page 369) before reading this section.


Every applet can run in multiple threads. The applet's GUI is created on the event-dispatching thread. The threads that the major milestone methodsinit, start, stop, and destroyare called from depends on the application that's running the applet. But no application ever calls them from the event-handling thread.

Many browsers allocate a thread for each applet on a page, using that thread for all calls to the applet's major milestone methods. Some browsers allocate a thread group for each applet, so that it's easy to kill 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 major milestone methods belongs to the same thread group.

Run a PrintThread applet[36] (Figure). PrintThread is a modified version of SimpleApplet that prints the thread and thread group that its init, start, stop, destroy, and update methods are called from.[37]

[36] tutorial/deployment/applet/examples/threads.jar

[37] tutorial/deployment/applet/examples/PrintThread.java

18. A screenshot of the PrintThread applet.


As usual, to see the output for the methods such as destroy that are called during unloading, you need to look at the standard output. For standard output for an applet run in a browser, open the Java Console from the browser's Tools menu. See the Displaying Diagnostics to the Standard Output and Error Streams section (page 583) for information about the standard output stream.

So why would an applet need to create and use its own threads? Imagine an applet that performs some time-consuming initializationloading images, for examplein its init method. The thread that invokes init cannot do anything else until init returns. In some browsers, this might mean that the browser can't 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, for example, then nothing would appear 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.

Rule of Thumb

If an applet performs a time-consuming task, it should create and use its own thread to perform that task.


Applets typically perform two kinds of time-consuming tasks: tasks that they perform once and tasks that they perform repeatedly. The next section gives an example of both.

Threads in Applets: Examples

This section discusses two examples of using threads in applets. The first applet, AnimatorApplet, shows how to use a thread to perform repeated tasks. The second applet this page discusses, SoundExample, shows how to use threads for one-time initialization tasks. SoundExample is featured in the Playing Sounds section (page 568).

This section does not explain basic thread code. To learn about the Java implementation of threads, refer to the Defining and Starting a Thread section (page 371).

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. (See the Using a Server to Work Around Security Restrictions section, page 591, for such an example.)

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 stops the applet's thread. When the user returns to the applet's page, the start method is called again, and the applet can again create a thread to perform the repetitive task.

Below is AnimatorApplet's implementation of the start and stop methods:

public void start() {
  if (frozen) {
      // Do nothing. The user has requested that we
      // stop changing the image.
  } else {
      // 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. We'll discuss AnimatorApplet's run method more a little later.

Notice that nowhere in the AnimatorApplet class is the Thread stop method called. This is because 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 will gracefully exit when you tap it on the shoulder. This shoulder tap comes in the form of setting to null an instance variable of type Thread.

In AnimatorApplet, this instance variable is called animatorThread. The start method sets it to refer to the newly created Thread object. When the applet needs to kill the thread, it sets animatorThread to null. This kills the thread not by making it be garbage collectedit can't be garbage collected while it's runnablebut 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() {
  ...
  while (Thread.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. If, on the other hand, animatorThread is null, the thread exits. If animatorThread refers to another thread, then a race condition has occurred: 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 reached the top of its while loop. Whatever the cause of the race condition, this thread should exit.

For more information about animation applets, see Threads in Applets, a section in Creating a GUI with JFC/Swing.

Using a Thread to Perform One-Time Initialization

If your applet needs to perform some initialization task that can take a while, you should consider ways of performing the initialization in a thread. For example, anything that requires making a network connection should generally be done in a background thread. Fortunately, GIF, PNG, and JPEG image loading is automatically done 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 current implementations, the Applet getAudioClip methods don't return until they've loaded all the audio data. As a result, if you want to preload sounds, you might want to create one or more threads to do so.

Using a thread to perform a one-time initialization task for an applet is a variation of the classic producer/consumer scenario. The thread that performs the task is the producer, and the applet is the consumer. The Synchronization section (page 377) discusses how to use Java threads in a producer/consumer scenario.

SoundExample adheres closely to the model presented in Synchronizing Threads. Like the Synchronizing Threads example, SoundExample features three classes:[38]

[38] Refer to footnotes on page 570.

  • The producer: SoundLoader, a THRead subclass.

  • The consumer: SoundExample, an Applet subclass. Unlike the Synchronizing Threads consumer example, SoundExample is not a Thread; it doesn't even implement the Runnable interface. However, the SoundExample instance methods are executed by at least two threads, depending on the application that executes the SoundExample applet.

  • The storage object: SoundList, a Hashtable subclass. Unlike CubbyHole in the Synchronizing Threads example, SoundList can return null values if the sound data hasn't been stored yet. This makes sense for this applet because it needs to be able to react immediately to a user request to play the sound, even if the sound hasn't been loaded yet.

For more information on SoundExample, refer to the Playing Sounds section (page 568).

Working with a Server-Side Application

Applets, like other Java programs, can use the API defined in the java.net package to communicate across the network. The only difference is that, for security reasons, the only host an applet can communicate with is the host it was delivered from.

Note

Depending on the networking environment an applet is loaded into, and depending on the browser that runs the applet, an applet might not be able to communicate with its originating host. For example, browsers running on hosts inside firewalls often cannot get much information about the world outside the firewall. As a result, some browsers might not allow applet communication to hosts outside the firewall.


It's easy to find out which host an applet came from. Just use the Applet getCodeBase method and the java.net.URL getHost method, like this:

String host = getCodeBase().getHost();

Once you have the right host name, you can use all the networking code that is documented in the Custom Networking trail in the online tutorial.[39]

[39] tutorial/networking/index.html

Note

Not all browsers support all networking code flawlessly. For example, one widely used browser compatible with Java technology doesn't support posting to a URL.


A Simple Network Client Applet

This section talks about the client to be an applet.[40] The client has been written not only to communicate with the host the applet came from, but also to have a graphical UI, and to have a loop so that it can get as many quotes as you like. You can run the applet by including it in a page with the following HTML code:

[40] tutorial/deployment/applet/examples/QuoteClientApplet.java

<APPLET CODE=QuoteClientApplet.class WIDTH=500 HEIGHT=100>
</APPLET>

The quoteApplet.html page[41] contains the above HTML code. By saving this page to a file on your local HTTP server, you can use it to communicate with the server-side application that will be running on the HTTP server. You must also save the compiled form of the applet to the same directory.

[41] tutorial/deployment/applet/examples/quoteApplet.html

Before the applet can get quotes, you need to run the server on the host that the applet came from. You then need to note the number of the port that the server is listening on. After you enter this port number into the applet, it will hook up with the server and you'll be able to get one-line quotations. Below are detailed instructions, followed by pictures of the server and the applet in action:

1.
Compile QuoteServer.java[42] and QuoteServerThread.java.[43] A text file (one-liners.txt[44]) should be in the same directory as the resulting class files.

[42] tutorial/deployment/applet/examples/QuoteServer.java

[43] tutorial/deployment/applet/examples/QuoteServerThread.java

[44] tutorial/deployment/applet/examples/one-liners.txt

2.
On the computer that serves the applet class file (through HTTP), invoke the interpreter on the QuoteServer class. For example, if you view the applet's page with the URL http://mymachine/quoteApplet.html, then you need to run the server on the host named mymachine.

3.
Record the port number that the quote server displays.

4.
Enter this number into the applet's text field.

5.
Press the Send button to request a quote from the server. You should see a quote appear in the text area.

Figure shows a picture of the applet in action.

19. A screenshot of the QuoteServer applet.


Using a Server to Work Around Security Restrictions

As the What Applets Can and Can't Do section (page 558) explains, applets are subject to many security restrictions. For example, they can't perform file I/O, they can't make network connections except to their original host, and they can't start programs.

One way of working around these restrictions is to use a server application that executes on the applet's host. The server won't be able to get around every applet restriction, but it can make more things possible. For example, a server probably can't save files on the host the applet's running on, but it will be able to save files on the host the applet originated from.

This section features an example of a server that allows two applets to communicate with each other. The applets don't have to be running on the same page, in the same browser, or on the same computer. As long as the applets originate from the same computer, they can communicate through the server that's running on that originating computer. The example uses sockets, which are documented in All About Sockets.[45]

[45] tutorial/networking/sockets/index.html

Here are the source files:

TalkClientApplet.java[46] The source file for the client applet. After you compile it, you can run it by including it in an HTML page with this tag:

[46] tutorial/deployment/applet/examples/TalkClientApplet.java

<APPLET CODE=TalkClientApplet.class WIDTH=550 HEIGHT=200>
</APPLET>

The talk.html page[47] includes the above HTML code. After saving this page to a file on your local HTTP server, you can use it to communicate with the talk server.

[47] tutorial/deployment/applet/examples/talk.html

TalkServer.java[48] and TalkServerThread.java[49] The source files for the server applet. After compiling both files, you can run the server on the applets' originating host by invoking the interpreter on the TalkServer class.

[48] tutorial/deployment/applet/examples/TalkServer.java

[49] tutorial/deployment/applet/examples/TalkServerThread.java

The instructions for running the server are just like those for the previous example (see the A Simple Network Client Applet section, page 590). Run the server on the applets' originating host, recording the port number that the applets should rendezvous on. Then initialize both applets (which can be running on different machines) to talk to the server port number. After this initialization is complete, type a string into each applet's text field. Then press the Return key to send the message to the other applet.

Here's the server in action:

www%  java TalkServer
TalkServer listening on rendezvous port: 36567

Figures 18.20 and 18.21 show pictures of the applets in action.

20. Sample talk outputmachine #1.


21. Sample talk outputmachine #2.




 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows