Chapter 2

Embedding Applets in Web Pages

by Mark Wutka


CONTENTS

While Java has been one of the hottest new technologies to hit the Web, it is still unfamiliar to many users. There are still only a handful of Web browsers that support Java. The rest simply ignore the <APPLET> tag that identifies an applet to the browser. For users whose browsers don't support Java, you want to provide at least some suggestion that there is something there that the browser cannot display. Otherwise, they might not realize that they are missing something. You also need to consider the fact that some applets may take a while to download. Most browsers just display a large blank area while downloading an applet, not giving the user an indication that there is anything more to display. You need to let the user know that there is something more to see and that they should be patient.

No Java? No Problem

Once you've written a Java applet and you want to display it in a Web page, you use the <APPLET> tag. This is probably one of the first things you learned when you started programming in Java. Making the jump from a simple "Hello World" applet to enhancing your company's Web page is a big step. You now have to consider the possibility that people cannot run Java. Many Web browsers still do not understand the <APPLET> tag. A browser that does not understand the <APPLET> tag simply skips over it. On the other hand, a browser that does understand the <APPLET> tag skips over any other tags and text up to the closing </APPLET> tag (except for the <PARAM> tags, of course). You can take advantage of this by providing alternative content, such as an image or text, to go in place of the applet.

Note
The most popular Java-enabled Web browsers are the Netscape Navigator (version 2 and later) and Microsoft Internet Explorer (version 3). Sun's HotJava browser not only runs Java, it is written entirely in Java.

Displaying an Image in Place of an Applet

You should consider displaying an image in place of an applet if your Web page expects something to occupy the applet's space. In other words, sometimes when you lay out your Web page, you place the applet somewhere and expect it to occupy a certain amount of space. The rest of the text is laid out accordingly. If you suddenly try to view the page on a browser that doesn't understand the <APPLET> tag, there will be nothing occupying that space and your page layout will be far from what you expected. You can still reserve that space, however, by using an image as the alternative content. The advantage of an image is that you can specify its exact size the same way you can with an applet. In fact, you may have noticed already that the <APPLET> tag contains a number of options that are identical to those of the <IMG> tag. If the image you want to display is smaller than the applet's display area, you can either expand the image or you can put padding around the image. Unfortunately, some older browsers, like Mosaic, won't even expand images, so if you are trying to accommodate really old browsers, you may want to avoid expanding the image.

Note
Some of the non-Java browsers you are likely to encounter are Netscape version 1, Microsoft Internet Explorer version 2 and earlier, Mosaic, and most custom browsers offered by Internet providers. In addition, Netscape version 3 does not support Java under Microsoft Windows version 3.

Listing 2.1 shows a Web page that displays an applet with an image as the alternative content. The image being displayed is only 100¥100 pixels, but the applet is 200¥200. The <IMG> tag specifies a width and height of 200, causing the browser to expand the image to fit the area.


Listing 2.1  Source Code for PushButton.html
<HTML>
<TITLE> Pushbutton Applet </TITLE>
<HEAD>
</HEAD>
<BODY>
Here is some text before the applet.
<APPLET codebase="." code="PushButton.class" width=200 height=200>
<IMG src="javacup.gif" width=200 height=200>
</APPLET>
Here is some text after the applet.
</BODY>
</HTML>

Figure 2.1 shows this page with the applet running.

Figure 2.1 : A Java-enabled browser displays the applet defined by the <APPLET> tag.

Figure 2.2 shows how this page looks on a browser that doesn't understand the applet tag but is still able to expand images.

Figure 2.2 : A browser that does not support Java can display an image in place of the applet.

You can also match the image size to the applet size by adding padding to the image. This might be useful on browsers that cannot expand images to fit a particular size. Unfortunately, these browsers might not support the HSPACE and VSPACE attributes. Mosaic, for instance, doesn't expand images and it doesn't support HSPACE and VSPACE. Listing 2.2 shows a Web page that contains the same 200¥200 applet and uses the same 100¥100 image as the alternative content. Rather than expanding the image to fit a 200¥200 space, it pads the image by 50 pixels on each side, making the effective image size 200¥200.


Listing 2.2  Source Code for PushButton2.html
<HTML>
<TITLE> Pushbutton Applet </TITLE>
<HEAD>
</HEAD>
<BODY>
Here is some text before the applet.
<APPLET codebase="." code="PushButton.class" width=200 height=200>
<IMG src="javacup.gif" width=100 height=100 hspace=50 vspace=50>
</APPLET>
Here is some text after the applet.
</BODY>
</HTML>

Figure 2.3 shows how the page appears on browsers that do not support applets.

Figure 2.3 : The HSPACE and VSPACE attributes add padding around an image.

Note
Figures 2.1, 2.2, and 2.3 feature the Microsoft Internet Explorer browser. Figure 2.1 uses Internet Explorer version 3, while Figures 2.2 and 2.3 use IE version 2, which does not support Java. Since IE version 2 was shipped with Windows 95, there are many people still using it.

Passing Parameters to Applets

One of the many ways you can strive to make reusable applets is to make them very configurable. You want to be able to use the same applet in a number of Web pages without creating multiple versions of the applet. You can use the parameter mechanism to pass parameters from a Web page to the applet.

Tip
If you are able to reuse an applet on a Web page rather than use two separate applets, you save a lot of downloading time. The browser doesn't download the same applet twice.

One of the difficult aspects of the getParameter method in the Applet class is that it has only one option-it fetches a single named parameter. There is no support for having multiple values for a parameter or for having a default parameter value. You can solve this by creating a class that performs these functions for you.

The Parameters class presented here provides a more flexible interface to applet parameters. It allows you to specify a default value for a parameter and also retrieve an array of values for a parameter. You can provide multiple values one of two ways-either by grouping them into one string with one or more separators or by providing <PARAM> tags with parameter names that have numbers appended to them.

If you want to supply multiple parameter values from a single <PARAM> tag, you need to define the set of separator values that you will put in between the parameters. The following example of the <PARAM> tag provides multiple values separated by colons:

<PARAM name="foo" value="somevalue:anothervalue:thisvalue:lastvalue">

You can also provide multiple parameters by appending numbers to the parameter name, starting at 0:

<PARAM name="foo0" value="somevalue">
<PARAM name="foo1" value="anothervalue">
<PARAM name="foo2" value="thisvalue">
<PARAM name="foo3" value="lastvalue">

The numbered parameters are more useful when you want to have several sets of multiple parameters. You may have a scrolling marquee, for instance, in which you supply the marquee text and the speed. You would like to be able to group them like this:

<PARAM name="text0" value="This is my marquee">
<PARAM name="speed0" value="100">

<PARAM name="text1" value="I hope you like it">
<PARAM name="speed1" value="200">

Listing 2.3 shows the source code for the Parameters class.


Listing 2.3  Source Code for Parameters.java
import java.util.Vector;
import java.util.StringTokenizer;
import java.applet.Applet;

/**
 * Provides extra ways to access applet parameters. Allows multiple
 * values for a parameter either by separated values, or by adding an
 * index value to each parameter (i.e. param0=first param1=second, etc.)
 * You can either call static methods or create an instance and call
 * instance methods. The static methods require that you pass the
 * applet each time. Because you create instances by passing the Applet
 * to the constructor, the instance methods don't require you to pass
 * the applet.
 * @author Mark Wutka
 */

public class Parameters
{
     protected Applet applet;

/**
 * Creates a Parameters instance that will fetch parameters for a
 * particular applet.
 * @param applet The applet whose parameters will be retrieved.
 */

     public Parameters(Applet applet)
     {
          this.applet = applet;
     }

// All the instance methods just pass through to the static methods
// to avoid code redundancy.

/**
 * Returns the named parameter, or null if not set (this is identical
 * to the Applet.getParameter method).
 * @param paramName The name of the parameter to retrieve.
 */
     public String getParameter(String paramName)
     {
          return getParameter(paramName);
     }

/**
 * Returns the named parameter, or defaultValue if not set.
 * @param paramName The name of the parameter to retrieve.
 * @param defaultValue The parameter value to use if there is no
 *          PARAM tag for this parameter.
 */
     public String getParameter(String paramName, String defaultValue)
     {
          return getParameter(applet, paramName, defaultValue);
     }

/**
 * Returns an array of parameters. The corresponding parameter names
 * in the PARAM tags should end with numbers starting with 0. For
 * example, for a parameter named "foo", the PARAM tags should use names
 * of foo0, foo1, foo2, etc:
 * <PRE>
 * <PARAM name="foo0" value="somevalue">
 * <PARAM name="foo1" value="anothervalue">
 * <PARAM name="foo2" value="thisvalue">
 * <PARAM name="foo3" value="lastvalue">
 * </PRE>
 * If you skip a number, the rest of the parameters will be ignored.
 *
 * @param paramName The base name for the parameters to be fetched.
 */

     public String[] getParameters(String paramName)
     {
          return getParameters(applet, paramName);
     }

/**
 * Returns an array of parameters. The parameters should be separated
 * by a specific separator character, or a set of separator characters.
 * For example, for the following call to getParameters:
 * <PRE>
 *    param.getParameters("foo", ";:,");
 * </PRE>
 * You could use :, ;, and , as separators:
 * <PRE>
 * <PARAM name="foo" value="somevalue:anothervalue;thisvalue,lastvalue">
 * </PRE>
 *
 * @param paramName The name of the parameter to fetch.
 * @param separators A string containing the separators for the parameters.
 */
     public String[] getParameters(String paramName, String separators)
     {
          return getParameters(applet, paramName, separators);
     }

/**
 * Returns the named parameter, or null if not set (this is identical
 * to the Applet.getParameter method).
 * @param applet The applet whose parameters will be fetched.
 * @param paramName The name of the parameter to retrieve.
 */
     public static String getParameter(Applet applet, String paramName)
     {
          return applet.getParameter(paramName);
     }

/**
 * Returns the named parameter, or defaultValue if not set.
 * @param applet The applet whose parameters will be fetched.
 * @param paramName The name of the parameter to retrieve.
 * @param defaultValue The parameter value to use if there is no
 *          PARAM tag for this parameter.
 */
     public static String getParameter(Applet applet, String paramName,
          String defaultValue)
     {
          String returnValue = applet.getParameter(paramName);
          if (returnValue == null) return defaultValue;
          return returnValue;
     }

/**
 * Returns an array of parameters. The corresponding parameter names
 * in the PARAM tags should end with numbers starting with 0. For
 * example, for a parameter named "foo", the PARAM tags should use names
 * of foo0, foo1, foo2, etc:
 * <PRE>
 * <PARAM name="foo0" value="somevalue">
 * <PARAM name="foo1" value="anothervalue">
 * <PARAM name="foo2" value="thisvalue">
 * <PARAM name="foo3" value="lastvalue">
 * </PRE>
 * If you skip a number, the rest of the parameters will be ignored.
 *
 * @param applet The applet whose parameters will be fetched.
 * @param paramName The base name for the parameters to be fetched.
 */
     public static String[] getParameters(Applet applet,
          String paramName)
     {
// Put the parameters into a vector first, because you don't
// know how many parameters you are getting.
          Vector vec = new Vector();

          for (int i=0; true; i++) {
// Try getting next numbered parameter
               String paramStr = applet.getParameter(paramName+i);
// If it isn't there, you're done
               if (paramStr == null) break;
// If you got the parameter, add the it to the vector
               vec.addElement(paramStr);
          }

// Create a string array to hold the values
          String[] returnValues = new String[vec.size()];

// Copy the vector values into the new string array
          vec.copyInto((Object[])returnValues);

          return returnValues;
     }

/**
 * Returns an array of parameters. The parameters should be separated
 * by a specific separator character or a set of separator characters.
 * For example, for the following call to getParameters:
 * <PRE>
 *    param.getParameters("foo", ";:,");
 * </PRE>
 * You could use :, ;, and , as separators
 * <PRE>
 * <PARAM name="foo" value="somevalue:anothervalue;thisvalue,lastvalue">
 * </PRE>
 *
 * @param paramName The name of the parameter to fetch.
 * @param separators A string containing the separators for the parameters.
 */
     public static String[] getParameters(Applet applet,
          String paramName, String separators)
     {
          String paramStr = applet.getParameter(paramName);

// If the parameters weren't there, just return an empty array
          if (paramStr == null) {
               return new String[0];
          }

// Put the parameters into a vector first, because you don't
// know how many parameters you are getting.
          Vector vec = new Vector();

// The tokenizer will separate out the parameters from the string
          StringTokenizer tok = new StringTokenizer(paramStr,
               separators);

// Grab the parameters from the string
          while (tok.hasMoreTokens()) {
               vec.addElement(tok.nextToken());
          }

// Create a string array to hold the values
          String[] returnValues = new String[vec.size()];
// Copy the vector values into the new string array
          vec.copyInto((Object[]) returnValues);

          return returnValues;
     }
}

Tip
The getParameters method in the Parameters class deals with a common problem. It must create an array of strings without knowing ahead of time how many strings there are. To solve this problem, you store the strings in a vector. Once you have all the strings you need, you can create an array of strings, using the size method in the vector to determine how many strings there are. Once you create the array, you simply copy the strings from the vector to the array. The copyInto method in the Vector class will do the copying for you so you don't have to do it manually.

Improving Applet Startup Time

When you are creating a commercial Web page that is covered with Java applets, you want to get results to the user as quickly as possible. Web browsers tend to leave a big blank spot on the page where an applet is supposed to go. If your applet takes 30 seconds to load, someone may not be patient enough to wait for it. In fact, he may not even know there is an applet being loaded. It would be nice if you could at least print a message telling the user that an applet is being loaded that is going to blow his socks off, so he should sit in rapt anticipation for your wonderful-but-huge applet to be downloaded. As it turns out, this is a snap! You can create a very small applet that downloads the real applet in the background. The only trick to this is that when you create an applet manually, it doesn't know its code base or document base. It does, however, have a method called setStub that tells it where to go for this information. Almost every method in the AppletStub interface is implemented in the Applet class, which means that your loader applet can act as an applet stub for another applet. The only method in the AppletStub interface that isn't in the Applet class is appletResize. Your appletResize method can simply call the resize method in your loader applet, since that will perform the actual resize.

Tip
The appletResize method in this case is performing a technique called "delegation." The appletResize method delegates the responsibility of performing the resizing to the resize method. You often use this technique in object-oriented programming where one object has a set of methods that delegate their responsibility to methods in another object.

Listing 2.4 shows a quick applet loader that can be used to display information while you are loading a much larger applet. This applet is less than 2K in size, so it should take only a few seconds to load.


Listing 2.4  Source Code for QuickLoader.java
import java.applet.Applet;
import java.applet.AppletStub;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Label;

// This applet is responsible for loading another applet in the
// background and displaying the applet when it finishes loading.
// The name of the applet to load is supplied by a <PARAM> tag.
// For example:
// <PARAM name="applet" value="RealApplet">
// which would load an applet class called RealApplet
//
public class QuickLoader extends Applet implements Runnable, AppletStub
{
      String appletToLoad;
      Label label;
      Thread appletThread;

      public void init()
      {
// Get the name of the applet to load
            appletToLoad = getParameter("applet");

// If there isn't one, print a message
            if (appletToLoad == null) {
                  label = new Label("No applet to load.");
            } else {
                  label = new Label("Please wait - loading applet "+
                        appletToLoad);
            }
            add(label);
      }

      public void run()
      {
// If there's no applet to load, don't bother loading it!
            if (appletToLoad == null) return;

            try {

// Get the class for the applet we want
                  Class appletClass = Class.forName(appletToLoad);

// Create an instance of the applet
                  Applet realApplet = (Applet)appletClass.newInstance();

// Set the applet's stub - this will allow the real applet to use
// this applet's document base, code base, and applet context.
                  realApplet.setStub(this);

// Remove the old message and put the applet up

                  remove(label);

// The grid layout maximizes the components to fill the screen area - you
// want the real applet to be maximized to our size.

                  setLayout(new GridLayout(1, 0));

// Add the real applet as a child component
                  add(realApplet);

// Crank up the real applet
                  realApplet.init();
                  realApplet.start();
            } catch (Exception e) {

// If we got an error anywhere, print it
                  label.setText("Error loading applet.");
            }

// Make sure the screen layout is redrawn
            validate();
      }

      public void start()
      {
            appletThread = new Thread(this);
            appletThread.start();
      }

      public void stop()
      {
            appletThread.stop();
            appletThread = null;
      }

// appletResize is the one method in the AppletStub interface that
// isn't in the Applet class. You can use the applet resize
// method and hope it works.

      public void appletResize(int width, int height)
      {
            resize(width, height);
      }
}

Once you are able to embed applets on a Web page, you can concentrate on the really fun part-creating interesting applets. Make your applets interesting, visually pleasing, and fast. And remember that not everyone will be able to run Java. Make sure you leave something for those poor souls.