Chapter 4: The Applet Class

Back in chapter 1, we wrote a simple applet, and in chapter 3 we designed a system that presents itself on the World Wide Web by using an applet, the AgentLauncher, as its user interface.  Thus, we have an idea of what an applet is, and a reason to use it.  Now it’s time to get detailed.

In this chapter we’ll write some applets and talk about where they appear, what they look like, when they run, and how they interact with their environment.  When we’re done, we should have a good idea of just what happens when a browser plows into that <applet> tag in an html document.

Where Do Applets appear?

As we’ve seen, there’s only one way to get an applet to appear on the browser screen, by embedding an applet tag within an HTML document and then viewing that document.  Like every other HTML element, the placement of that applet tag determines where on the screen that applet appears.  Consider the simple example of Listing 4-1.

package chap4;

 

import java.awt.Graphics;

import java.applet.Applet;

 

/** An applet that prints the string "Hello world" at absolute

applet-relative coordinates x=20, y=50.

@author John Rodley

@version 1.0

*/

public class ch4_fig1 extends Applet {

 

/** Actually paint the string on the screen.

*/

  public void paint(Graphics g) {

       g.drawString( "Hello world", 20, 50 );

  }

}

 

Listing 4-1: Simple example surrounded by text paragraphs.

<!DOCTYPE HTML PUBLIC

"-//SQ//DTD HTML 2.0 HoTMetaL + extensions//EN">

<HTML><HEAD><TITLE>An Applet in the HTML Milieu</TITLE></HEAD>

<BODY><H1>Some Text</H1>

<P>When you want to place an applet, you simply place it

within the page at the spot you want it to appear.  The

problem of placing applets is the same problem all HTML

elements have - the fact that HTML tags are 'suggestions', not

directives, which browsers are free to interpret

differently.  Now, a pair of goodogs, for your amusement:

<IMG SRC="chap4/ch4_fig1a.jpg" ALIGN="BOTTOM">

<IMG SRC="chap4/ch4_fig1b.jpg" ALIGN="BOTTOM">

<P>Below is the applet.  Can you tell the difference between

the applet text, and the HTML?</P>

<applet code=chap4/ch4_fig1.class width=100 height=100>

</applet>

The applet is above us.  Notice that as long as you don't

use non-default backgrounds, it's impossible to tell the

applet from the HTML.</P></BODY></HTML>

 

 

Listing 4-1a: An HTML page referencing listing 1.

There are a number of HTML elements including text paragraphs and images, then our simple applet, then more HTML elements.  Figure 4-1 shows how this page appears under Netscape.

Figure 4-1: Netscape running Listing 4-2.

<ch4_fig1.tif>

The package and import statements were covered back in chapter 2.  The two imports we need here are for the Applet class, which all applets sub-class, and for the Graphics class, which we use in the paint method.  The line ‘extends Applet’ tells the compiler that this class should get all the funtionality of the Applet class.  The paint method overrides a do-nothing method that is part of the Applet class (via its inheritance of Component).  The line g.drawString tells the Graphics object to draw a String at 20 on the X-axis and 50 on the Y.  If you think about it, 6 lines of code (3 of which are pro-forma) has gotten us a functional, and useful applet.  We’ll talk more about the paint method later in this chapter, and cover the details of dealing with Graphics objects in more detail in Chapter 5.  For now, what we’re mainly interested in is how the applet fits into the host HTML document.

The rule which browsers use when formatting a document is to stretch or compress the document vertically such that a line of text never rolls off the horizontal edge of the screen. If you resize the screen, Netscape will reformat the text AND reposition the applet. Thus, as we resize the document of figure 4-1 to make it smaller horizontally, eventually our applet is pushed out the bottom of the window and we have to scroll vertically to get it back on the screen. 

If you’ve played with browsers much, you’ve probably seen that browsers treat text and images differently.  For instance, if you shrink the document of figure 4-1 such that the window is smaller horizontally than the width of our images, you’ll the browser add a horizontal scroll bar so that we can scroll to see the entire image.  Browsers treat applets the same as images - as lumps that always get a rectangular area whose shape can’t be changed by the browser.

What Do Applets Look Like?

So far, we’ve talked about applets in general terms as a small application, an app-let.  But in Java, Applet is an actual class of its own, with very specific capabilities. In fact, the Applet class is at the end of a fairly long chain of inheritance consisting of Object, Component, Container, Panel, and Applet.  In simplistic terms, the first four super-classes give Applet the following capabilities:

Object         The ability to behave as a Java entity

Component The ability to appear, on-screen, as a visual entity

Container     The ability to ‘encapsulate’ other visual entities

Panel           The ability to align encapsulated visual entities

We’ve already talked a little about the basic Java Object.  The next three classes are part of the windowing system, AWT, which we’ll discuss briefly here, and in more detail in chapter 5.  From these super-classes, we can easily infer a lot about what an applet is - it’s a space on the screen, a window if you wish, that is a child of the window that the browser is displaying the HTML text in.

Component is the key class in this inheritance chain.  Sub-classing Component makes Applet a child window.  This has very detailed and specific meaning to the base GUI (Windows, or X11).  Here, we can use a much simpler definition.  Visually, a child window is a space on the screen that has the same background color as its parent, the browser main window.  It has no border, and no menu, and nothing appears in that window unless we put it there.  It can be drawn on at the pixel level (via Graphics) and it has the ability to capture events, like mouse clicks and key presses.

The Applet class is not just the sum of its super-classes, however.  It implements many methods of its own, as shown in Table 4-1.

Table 4-1: the Applet class.

Method Argument          Return

getApplet          String   Applet

            The String name of the Applet.   The Applet, or null if It isn’t loaded.

getAppletContext                      AppletContext

                        An object implementing the AppletContext interface.

getAppletInfo                String

                        Whatever information string the applet author chooses to return.  Usually author, version, copyright.

getAudioClip     URL or URL,String         AudioClip

            The URL of a .au audio stream data file. An in-memory audio clip that can be played via Applet.play.

getCodeBase                URL

                        The complete URL of this applet.

getDocumentBase                     URL

                        The complete URL of the HTML document this applet is embedded in.

getImage          URL or URL, String        Image

            The URL of a GIF or JPEG image file.    An in-memory image that can be drawn via Graphics.drawImage.  This call DOES NOT LOAD THE IMAGE FILE OVER THE NET.

getParameter    String   String

            A string matching exactly the string in the  <param name=XXX value=YYY> section of the applet HTML tag of the HTML document            The string provided in the value= portion of the param statement.  For the example:

<param name=GHIJ value=1234> a call to getParameter(“GHIJ”) returns
“1234”.

getParameterInfo                       String [][]

                        A two dimensional String array  explaining the arguments this applet accepts.

init                   

                        Overload method that gets called whenever the applet is loaded.

isActive                        boolean

                        true if Applet.start has been called

play      URL or URL,String        

            The URL of a .au audio data stream file.

resize    int height, width

            The height and width to resize the applet to.      

showStatus       String  

            The string to display in the browsers status bar. 

start                  Overloaded method called each time the user ‘visits’ the applets HTML page.

stop                 Overloaded method called each time the user ‘leaves’ the applets HTML page.

Over the course of this chapter we'll use, and talk a little about each of these methods, except getImage and the URL grabbing methods, getDocumentBase and getCodeBase.  If you’re psyched to start displaying images via getImage, you’ll have to wait until chapter 5.  getDocumentBase and getCodeBase are both used extensively in Chapter 7.

How Big is an Applet?

The size of an applet is set through the applet tag in the HTML document. In listing 4-1, we specified a height of 100 pixels and a width of 100 pixels, so the browser made a hole of that size in the document and flowed the text around it. 

Some of the current crop of browsers don’t deal well with reformatting the HTML document if the applet changes size.  Say that you use Applet.resize to make your applet smaller than the size set from the applet tag with height/width.  The HTML document should re-flow to fit exactly around the new, smaller size.  Listing 4-2 changes the simple applet to downsize itself, use a background color other than the default for the applet, and add a background image to the HTML document. If the browser doesn’t re-flow the HTML document, there’ll be an embarassing strip of default background in the spaces around the applet.

 

 

package chap4;

 

import java.awt.Graphics;

import java.applet.Applet;

import java.awt.Color;

 

/** An applet that prints the string "Hello world, again" at

absolute applet-relative coordinates x=20, y=50 after clearing

the applet workspace to yellow.

@author John Rodley

@version 1.0

*/

public class ch4_fig2 extends Applet {

 

/** Resize the applet to slightly smaller than the applet

tag sizes us.

*/

  public void init() {

    resize( 90, 90 );

  }

 

/** Actually paint the string on the screen, first clearing the

workspace to yellow.

*/

  public void paint(Graphics g) {

    g.setColor(Color.yellow);

    g.fillRect(0,0,size().height, size().width);

    g.setColor(Color.black);

       g.drawString( "Hello world, again", 20, 50 );

  }

}

<!DOCTYPE HTML PUBLIC

"-//SQ//DTD HTML 2.0 HoTMetaL + extensions//EN">

<HTML><HEAD><TITLE>The Shrivelling Applet</TITLE></HEAD>

<BODY BACKGROUND="background.jpg">

<P>A pair of goodogs, to watch your incredible shrinking

applet:

<IMG SRC="chap4/ch4_fig1a.jpg" ALIGN="BOTTOM">

<IMG SRC="chap4/ch4_fig1b.jpg" ALIGN="BOTTOM">

Below is the applet, started with an initial size of 100 by

100 pixels:

<applet code=chap4/ch4_fig2.class width=100 height=100>

</applet>

The first word of this sentence should butt right up against

the applets right hand edge even though the applet has shrunk

to 90 by 90 pixels, assuming the browser re-flowed correctly. 

</P></BODY></HTML>

 

 

Listing 4-2: A ‘shrivelled’ applet and an HTML document that uses it.

 

Figure 4-2: The shrivelled applet under Netscape.

<ch4_fig2.tif>

So that’s what happens if you shrink an applet within its allotted browser space.  What if you grow it beyond it’s browser space?  It’s the same issue in reverse: the HTML document has to be re-flowed so that there’s more space for the growing applet.  Listing 4-3 changes the applet of listing 2 to burst out of its allotted space, and figure 4-3 shows the results.

package chap4;

 

import java.awt.Graphics;

import java.applet.Applet;

import java.awt.Color;

 

/** An applet that prints the string "Hello world, again" at

absolute applet-relative coordinates x=20, y=50 on a red

background.

@author John Rodley

@version 1.0

*/

public class ch4_fig3 extends Applet {

 

/** Resize the applet to slightly larger than the applet

tag sizes us.

*/

  public void init() {

    resize( 120, 120 );

  }

 

/** Actually paint the string on the screen, after clearing the

applet to red first.

*/

  public void paint(Graphics g) {

    g.setColor(Color.red);

    g.fillRect(0,0,size().height, size().width);

    g.setColor(Color.black);

       g.drawString( "Hello world, again", 20, 50 );

  }

}

 

 

<!DOCTYPE HTML PUBLIC

"-//SQ//DTD HTML 2.0 HoTMetaL + extensions//EN">

<HTML><HEAD><TITLE>The Growing Applet</TITLE></HEAD>

<BODY BACKGROUND="background.jpg">

<P>A pair of goodogs, to watch over your growing applet:

<IMG SRC="chap4/ch4_fig1a.jpg" ALIGN="BOTTOM">

<IMG SRC="chap4/ch4_fig1b.jpg" ALIGN="BOTTOM">

Below is the applet, started with an initial size of 100 by

100 pixels:

<applet code=chap4/ch4_fig3.class width=100 height=100>

</applet>

The first word of this sentence should butt right up against

the applets right hand edge even though the applet has grown to

120 by 120 pixels, assuming the browser re-flowed correctly. 

</P></BODY></HTML>

 

Listing 4-3: The growing applet.

 

Figure 4-3: The growing applet under Netscape.

<ch4_fig3.tif>

As you can see, Netscape correctly re-flows the document around whatever applet re-sizing you might do.  Notice also that our text string doesn’t completely print out.  This is the fate of graphics operations that try to exceed the bounds of the applet, they simply don’t happen.  In chapter 5’s ticker example, we’ll take a closer look at how to fit text into a given area.

When Does an Applet Run?

For traditional computer programs the answer to this question is obvious.  When the user runs the program, it runs, and when the user closes it, it stops running.  The user doesn’t have that kind of control over an applet.  It is the browser, not the user, that loads, runs, stops and unloads applets, so the answer becomes a lot more involved. The simplistic answer to the question is, an applet runs when its space in the HTML document is visible, and stops running when, for whatever reason, that space in the doc is not on-screen.

Unless an applet creates and runs in its own thread, the simple answer holds true.  This is because an applet like the simple one of listing 4-1 is running in a thread that the browser has created and controls.  When Java applets create their own threads, the answer is more complicated.  Technically, because these threads are beyond the control of the browser, there is no written-in-stone rule on when an applet runs.  In theory, browsers expect well-behaved applets to adhere to the basic rule of run when you’re on-screen, and stop running when you’re off-screen, but whether or not an applet implements that theory is completely up to the applet writer.  We’ll discuss threads in more detail in chapter 6, and eventually write our own multi-threaded and ill-behaved, but wickedly useful, applets.

Whether the applet itself is single or multi-thread, ill or well-behaved, the browser obeys a certain set of rules when trying to run it.  In this sense, the browser treats the applet as a peer, where, no matter how the applet behaves, it can expect certain behaviors from the browser.  These behaviors, the browser invoking one of four applet methods, are clues as to what is going on in the browser.

When the browser first encounters the <applet> tag in the HTML doc, it calls Applet.init.  When the user visits the applet’s page, the browser calls Applet.start.  When the user leaves the applet’s page, the browser calls Applet.stop and whenever it decides the applet is not needed anymore and should be expunged from memory, it calls Applet.destroy.   Thus, Applet.init and Applet.destroy get called once in the life of an applet, while Applet.start and Applet.stop can be called any number of times.

 Figure 4-4: Overridden methods along the applet execution trail.

<ch4_fig4.tif>

This gives us a signpost at each significant point of the applet execution trail.  An applet that cares about whether or not it’s loaded or on-screen overrides init, start, stop and destroy.  In listing 4-4 we modify the simple applet to take arguments and sing for us.

package chap4;

 

import java.awt.Graphics;

import java.applet.Applet;

import java.applet.AudioClip;

import java.net.*;

 

/** A simple app that plays a tune, supplied by the applet tag

via the name "StartClip" every time Applet.start is called, and

plays another tune, paramter name "EndClip" every time

Applet.stop is called. Plays the RunningClip in a loop between

start and stop.

@version 1.0 12/20/1995

@author John Rodley

*/

public class ch4_fig4 extends Applet {

  String sAppletInfo;

  URL StartClipURL;

  URL EndClipURL;

  AudioClip RunningClip;

 

  String pinfo[][] = {

           {"StartClip", "URL", "The URL of an audio clip played at startup" },

           {"RunningClip", "URL",

        "The URL of an audio clip played between start and stop" },

           {"EndClip", "URL", "The URL of an audio clip played at stop" }

            };

 

/** Whenever a user visits our page, the browser calls this.

This is an override of an Applet method.. */

  public void start() {

    if( RunningClip == null || StartClipURL == null )

      return;

       play( StartClipURL );

    RunningClip.loop();

    System.out.println( "after loop" );

  }

 

/** Whenever the user leaves our page, this gets called.

Another override of an Applet method.

*/

  public void stop() {

    if( RunningClip == null || EndClipURL == null )

      return;

    RunningClip.stop();

     play( EndClipURL );

  }

 

/** Tell anyone who might inquire what we are.

@return A string describing this applet.

*/

  public String getAppletInfo() {

     return("JRodley - Java Applets Book - Chapter 4 figure 4");

       }

 

/** Tell anyone who might inquire how to use this applet.

@return A 2d string array describing the arguments to this

applet.

*/

  public String[][] getParameterInfo() {

       return pinfo;

     }

 

/** Connect to the URLs specified by the StartClip and EndClip

parameters in the applet tag.

*/  

  public void init() {

     try {

          StartClipURL = new URL(getParameter("StartClip"));

          RunningClip = getAudioClip(new URL(getParameter("RunningClip")));

           EndClipURL = new URL(getParameter("EndClip"));

          } catch( MalformedURLException e )

          { System.out.println("exception"+e );}

  }

}

 

 

<!DOCTYPE HTML PUBLIC

"-//SQ//DTD HTML 2.0 HoTMetaL + extensions//EN">

<HTML><HEAD><TITLE>The Singing Applet</TITLE></HEAD>

<BODY>

<applet code=chap4/ch4_fig4.class width=100 height=100>

<param name=StartClip value="http://www.coriolis.com/japp/chap4/charge.au">

<param name=RunningClip value="http://www.coriolis.com/japp/chap4/charge.au">

<param name=EndClip value="http://www.coriolis.com/japp/chap4/retreat.au">

</applet>

</P></BODY></HTML>

Listing 4-4: The do-nothing applet, with sound.

Play with the applet.  See what happens if you scroll the applet off screen, or if you hyperlink to another page.

Audio And Parameters

The basis of Java’s audio capability is the AudioClip class. In order to do any audio, you need to get the actual audio data from a file somewhere on the net.  Where is that file?  We could hardwire the filename into the class file, as in:

public final static string startClipFile =

new String( “http://www.channel1.com/users/ajrodley/start.au” );

but a more flexible way of dealing with data like this is to pass it in as an argument via the <param name=value=> construct within the HTML source.  In Listing 4-4, we’ve defined three arguments - StartClip, RunningClip and EndClip - each of which is the URL of an audio file somewhere out on the net.  In the init method, which as we’ve said is the first override method invoked by the browser, we query these arguments via Applet.getParameter.  When, in our HTML page, we say:

<param name=StartClip value=“http://www.channel1.com/users/ajrodley/start.au”>

the call

String s = getParameter(“StartClip”);

returns the String “http://www.channel1.com/users/ajrodley/start.au”.

Applets, as we’ll discuss later, operate in a multi-applet environment.  This is a good thing, but it imposes some responsibilities on well-behaved applets.  The first of these is the responsibility to document the arguments the applet accepts via the getParameterInfo method.  Listing 4-4 implements both this and the getAppletInfo method, which allows users of our applet to query the applet’s author, version and whatever else we want to put in the returned string.  These methods are not required, but are courtesies we extend to other applets that may want to know something about us, or to HTML and/or applet writers who may want to use our applet in their own code.

The singing applet uses two different methods for playing audio clips.  The start and end clips only need to play one time through, whenever their method (start or stop) is invoked, so we can play them simply by passing their URLs to Applet.play.  We want the running clip to play continuously, though.  For that we need to explicitly create an AudioClip object.  Thus, in our init method, we create three objects: URLs for the start and end clips, and an AudioClip for the running clip. 

The init method also contains a new wrinkle for us, an exception handling clause.  We deal with exceptions in more detail in chapter 8.  What you need to remember here is that if a method is defined as ‘throwing’ an exception any method that calls it has to either define itself as also throwing that exception, or it has to handle that exception with a try-catch statement.  This is what we do here.  The URL constructor throws MalformedURLException, so we catch it, and simply make a note of it on standard output.  The reason the URL constructor throws any exception is that it goes out onto the net and makes sure the specified URL is accessible.  Thus, if our init method runs without throwing an exception, we can be sure that there are accessible files at the URLs we specified.

When init is done, we have three objects: two URLs and one AudioClip.  The URL constructors only made sure there was a file at the URL but the AudioClip constructor actually fetched the data from the URL.  To play the start and end clips within the start and stop methods, we simply invoke the play method with the proper audio file’s URL as the argument.  To keep the running clip playing continuously while the applet is on-screen, we simply invoke AudioClip.loop in the start method.  AudioClip.loop returns immediately, even though the clip continues to play until AudioClip.stop in our applet’s stop method.

Listing 4-5 presents a little different version of this do-nothing applet.

 

package chap4;

 

import java.util.*;

import java.applet.*;

import java.awt.*;

 

/* A do-nothing applet that runs in its own thread.

 

@version x.xx 1.10, 1 August, 1995 xxx

@author John Rodley

*/

 

public class ch4_fig5 extends Applet implements Runnable {

 

/** The main thread */

Thread myThread;

String TheString;

int iteration = 0;

 

/** Start the main thread for this game. */

  public void start() {

    TheString = new String( "Applet is on screen now in " );

       if( myThread == null )

             {

           myThread = new Thread( this );

          myThread.start();

             }

     }

 

/** Stop this thread. */

  public void stop() {

       TheString =

      new String("Applet is off screen but still running in ");

  }

 

/** The main loop for the main thread. */

  public void run() {

           while( myThread != null )

                {

                try {

                Thread.sleep( 300 );

                     } catch( InterruptedException e )

          { System.out.println("exception" ); }

                showStatus( TheString+iteration+"th iteration ");

                iteration++;

                }

           myThread = null;

     }

}

 

Listing 4-5: The do-nothing applet in its own thread.

In this one we run the applet in its own thread.  The key things to note here are the start and run methods, and the fact that our applet implements the Runnable interface.  In the start method, we push the applet into its own thread by creating a new Thread object and passing the applet itself as the only argument to the constructor.  This is only possible because our applet implements Runnable. 

The only requirement of the Runnable interface is that you implement a run method, as we have here. Sometime after the call to myThread.start returns, the run method will begin executing asynchronously.  Within this run method, we simply sit in a loop, sleeping for 300 milliseconds, then incrementing a counter and displaying it in the browser status line.  Note that since we only want ONE of these independent threads to run, we check myThread within start.  Otherwise, every visit to this page would create a new thread with the predictable effect on system performance.

Run this applet under Netscape.  You’ll see the status string changing regularly.  Now hyperlink away to some other HTML page.  Notice that the status string keeps changing.  We have violated one of the rules of well-behaved applets.  Our host HTML document has disappeared, but our applet is still running!

Our new, do-nothing applet will continue to run for as long as the browser is up.  To kill it, you have to close Netscape.  This persistence presents ominous possibilities.  In listing 4-6, a simple change to listing 4-5 turns the do-nothing applet into a malicious do-nothing applet.

 

package chap4;

 

import java.util.*;

import java.applet.*;

import java.awt.*;

 

/* A malicious do-nothing applet that runs in its own thread,

eating up CPU cycles because it neither sleeps nor exits.

 

@version x.xx 1.10, 1 August, 1995 xxx

@author John Rodley

*/

 

public class ch4_fig6 extends Applet implements Runnable {

 

/** The main thread */

Thread myThread;

String TheString;

int iteration = 0;

 

/** Start the main thread for this game. */

  public void start() {

    TheString = new String( "Applet is on screen now in " );

       if( myThread == null )

             {

           myThread = new Thread( this );

          myThread.start();

             }

     }

 

/** Stop this thread. */

  public void stop() {

       TheString =

      new String("Applet is off screen but still running in ");

  }

 

/** The main loop for the main thread. */

  public void run() {

           while( myThread != null )

                {

                showStatus( TheString+iteration+"th iteration ");

                iteration++;

                }

           myThread = null;

     }

}

 

Listing 4-6: The malicious do-nothing applet, secretly eating CPU cycles.

If you run the applet of Listing 4-6 you will have to kill your browser to get rid of it.

We remove the sleep call from the run method, and simply sit in a loop incrementing a variable.  We also remove the call to showStatus that alerts the user that we are still running.  Run this applet in Netscape, then hyperlink away.  Notice how unresponsive the browser is.  The malicious applet is eating up a lot of CPU cycles.  The key features of this malicious applet are actually omissions, the fact that our run method never blocks, and that we didn’t implement an Applet.stop method that stops our thread.  All applets that run in their own thread must take special care to run only when, and as much as neccessary. 

By this point, you’ve seen the light and want to write well-behaved, threaded applets.  But how?  Listing 4-7 modifies the threaded do-nothing applet to stop running whenever the user leaves the page, and re-start whenever the user returns.

 

package chap4;

 

import java.util.*;

import java.applet.*;

import java.awt.*;

 

/* A do-nothing applet that runs in its own thread.

 

@version x.xx 1.10, 1 August, 1995 xxx

@author John Rodley

*/

 

public class ch4_fig7 extends Applet implements Runnable {

 

/** The main thread */

Thread myThread;

String TheString;

int iteration = 0;

 

/** Start the main thread for this game. */

  public void start() {

    TheString = new String( "Applet is on screen now in " );

       if( myThread == null )

             {

           myThread = new Thread( this );

          myThread.start();

             }

     }

 

/** Stop this thread. */

  public void stop() {

       TheString =

      new String("Applet is off screen but still running in ");

// This line is the key. If myThread is null, the run method

// returns and the thread disappears.   

    myThread = null;

  }

 

/** The main loop for the main thread. */

  public void run() {

           while( myThread != null )

                {

                try {

                Thread.sleep( 300 );

                     } catch( InterruptedException e )

          { System.out.println("exception" ); }

                showStatus( TheString+iteration+"th iteration ");

                iteration++;

                }

     }

}

 

Listing 4-7: The new, well-behaved do-nothing applet.

With a threaded applet, the thread the applet runs in only exists for as long as the run method is running.  What we need is code in our stop method (which the browser calls whenever the user leaves the page) that will cause the run method to break out of its loop.  This is what setting myThread to null does.  Then in the start method (called whenever the user visits our page) we re-create the thread and set it running.  We’ll talk more about threads in chapter 6.  In the meanwhile, the well-behaved, threaded, do-nothing applet will serve as a good template for any threaded applets we might need.

How Do Applets Interact With the Browser?

Prior to the beta version of Java, applets existed in a vacuum.  They had no way of knowing anything about any other applets.  Now, applets can call Applet.getAppletContext.  This returns an object that implements the AppletContext interface.  Usually this object will be the browser itself, but it might not be.  In fact, browsers that wish to protect themselves from rogue applets may create a separate class just to contain the applet context for this call.  In any case, it literally doesn’t matter to us, because, as with any interface, the definition of the interface is all that you can rely on.

The AppletContext, shown in Table 4-2, is the applet’s interface to the browser.  AppletContext provides only six methods - allowing an applet to get images and audio clips, learn about other applets active in the browser, and to show new HTML documents.

Table 4-2: The AppletContext Interface.

Method                  Argument                             Return

getApplet              String                                    Applet

                                The String name of the Applet.                                                              The Applet, or null if It isn’t loaded.

getApplets                                                           Enumeration

                                                                              A list of the Applets loaded.  Cast the members of the Enumeration to Applet to use them.

getAudioClip        URL                                      AudioClip

                                The URL of a .au file containing the audio stream.                            A playable audio stream.

getImage                URL                                      Image

                                The URL of a GIF or JPEG image file.                                                 An in-memory image that can be drawn via Graphics.drawImage.

showDocument     URL                                     

                                The URL of a document to hyperlink to.                                             

showStatus            String                                   

                                The message to be displayed in the status bar along the bottom of the browser window.                               

 

Using getAppletContext we now modify our well-behaved, threaded, do-nothing applet to get information about all the other running applets.  Listing 4-8 shows our new applet and the HTML page used to create four of these applets on one page. 

 

package chap4;

 

import java.util.*;

import java.io.*;

import java.net.*;

import java.applet.*;

import java.awt.*;

 

/** An applet that finds the other applets that exist within

this AppletContext, fights with them to write a string to the

browser status line, and writes a string directly into the

other applet windows.

 

@version 1.10, 11/20/95

@author John Rodley

*/

 

public class ch4_fig8 extends Applet implements Runnable {

 

 

/** The main thread */

Thread myThread = null;

String TheString = null;

String myName;

String paintString = null;

int iteration = 0;

int paintIteration = 0;

 

/** Start the main thread for this game. */

  public void start() {

       TheString = new String( "Our applet is on screen now in " );

     if( myThread == null )

          {

             myThread = new Thread( this );

           myThread.start();

          }

     }

 

 

/** Stop this thread. */

  public void stop() {

       myThread = null;

     }

 

/** The main loop for the main thread. */

  public void run() {

           while( myThread != null )

                {

                try {

                Thread.sleep( 300 );

                     } catch( InterruptedException e )

            { System.out.println("exception" ); }

                showStatus( myName+" "+TheString+iteration+

                      "th iteration "+paintString );

                repaint();

                iteration++;

                }

     }

 

/** Paint our window.  Display a string in this window for

each applet we find, then display a string in each of the other

applets windows.

*/

public void paint( Graphics g ) {

     String gString;

     int i = 0;

 

     // Label this applet with its name

     g.drawString( "Name: "+myName, 0, 20 );

 

     AppletContext ac = getAppletContext();

     Enumeration e = ac.getApplets();

     Dimension d = size();

     g.drawRect( 0, 0, d.width-5, d.height-5 );

     while( e.hasMoreElements()) {

           Applet a = (Applet)(e.nextElement());

           // If this applet is a ch4_fig8 then it has a name parameter set

           String s = a.getParameter("Name");

           if( s == null )

                s = new String( "not a ch4_fig8 applet" );

           Point p = a.location();

           gString = new String( "Applet at "+p.x+","+p.y+" "+s );

           // The list will always include "this", so check it

           if( a == this )

                gString = new String( gString+" this is us" );

           else {

                // Now draw a string into the other applets window

                Graphics g1 = a.getGraphics();

                g1.drawString( myName+" calling ...", 0, 40 );

                }

           // draw the string describing the other applet in our window

           g.drawString( gString, 10, 20*(i+3) );

    

           i++;

           }

     }

 

/** If the user clicks on the applet, stop it.

@return false

*/

  public boolean mouseDown(Event e, int x, int y) {

     stop();

       return( false );

  }

 

/** Initialize the applet. Resize it and give it a name.  Make

sure that the applet is 'named' via the applet tag.

*/

  public void init() {

     myName = getParameter( "Name" );

       if( myName == null )

             myName = new String( "Set the Name= parameter please!" );

     resize( 210, 210 );

     }

}

 

Listing 4-8: The simple applet modified to find any other applets.

 

Figure 4-5 shows the output of our new applet. 

 

Figure 4-5: The output of the new simple applet.

<ch4_fig5.tif>

The AppletContext interface provides the method getApplets which returns an Enumeration (basically a vector) containing all the Applets running in this AppletContext.  We retrieve each individual applet via Enumeration.nextElement. 

The Applet returned by getApplets is completely capable.  We can call any method available in the Applet class against that applet.  Thus, in our paint method, we call drawString against our own applet, and against each of the other applets we find.  As this page runs, you can see the four applets dueling over the status line and drawing over each other as they draw the intrusive string into each other’s graphics context.

In Listing 4-8, we introduce a new class, Point and a new interface, Enumeration.  Point simply encapsulates two ints, the X and Y coordinates of the point.  In C or C++ this would undoubtedly have been implemented as a simple struct.  Enumeration is a far more interesting concept.  What does it mean to enumerate something?  Here, enumeration means ‘to walk through a list using up the elements as you go’.  Essentially, an enumeration is a list that you can go through once and only once.  The interface requires only two methods, hasMoreElements and nextElement, which force you to use the same flow-control whenever you’re dealing with an Enumeration.

// assume e is an object that implements the Enumeration interface

while( e.hasMoreElements()) {

     Object o = e.nextElement();

     ...

     }

// At this point e has been ‘used up’.  It cannot be walked through again.

In Listing 4-8’s paint method, we get an Enumeration of Applets from the AppletContext interface method getApplets, then walk through it, dueling with each of the Applets it returns.  The line:

Applet a = (Applet )(e.nextElement());

also illustrates one of the powers of Java’s object orientation that might not be obvious.  Enumeration.nextElement always returns an Object, but a simple cast of that Object to Applet, gives us access to the much wider range of capabilities of the Applet class.  That Object was always an Applet.  It’s just that the Enumeration doesn’t CARE what class it’s elements are, so it stores them as Object and lets the user of the Enumeration deal with casting them back to the proper type.  What would happen if, by some chance, the call to e.nextElement returned an Object that was NOT of the Applet class?  The Java interpreter would throw an IllegalCastException.  This is something you have to consider very carefully whenever you cast from a base class to a superclass, especially when you’re using Vectors or Enumerations you get from someone else’s objects.

With that bit of type-casting wisdom in mind, consider the following lines from listing 4-8:

Applet a = (Applet)(e.nextElement());

// If this applet is a ch4_fig8, then it has a name parameter set

String s = a.getParameter(“Name”);

if( s == null )

     s = new String( “not a ch4_fig8 applet”);

The point of all that is simply to determine whether we’re dealing with the proper class of object, a ch4_fig8.  There’s a much better way to deal with this:

Applet a = (Applet)(e.nextElement());

// If a is not a ch4_fig8, move on to the next applet

if( a instanceof ch4_fig8 )

     ...

else

     continue;

The instanceof operator tells us whether Object a is an instance of class ch4_fig8.  Checking objects with the instanceof operator is usually a good idea BEFORE you try a type-cast.  In this particular instance, AppletContext.getApplets guarantees that it will return an Enumeration of Applets, so the cast to Applet should be safe, but beyond that, we can’t be sure, so we use instanceof.

AppletContext.getApplets gives us an inter-applet communication mechanism for which there are any number of  uses.  As another brutally simple example, let’s modify the dueling applet of Listing 4-8 so that it kills off all the other applets on a page via Applet.stop, thus ensuring that it’s the only running applet on a page.

 

package chap4;

 

import java.util.*;

import java.applet.*;

import java.awt.*;

 

/** An applet that finds the other applets that exist within

this AppletContext, and stops them.

 

@version 1.10, 11/20/95

@author John Rodley

*/

 

public class ch4_fig9 extends Applet implements Runnable {

 

/** The main thread */

Thread myThread = null;

String TheString = null;

int iteration = 0;

int paintIteration = 0;

 

/** Start the main thread for this game. */

  public void start() {

       TheString = new String( "Our applet is on screen now in " );

     if( myThread == null )

          {

             myThread = new Thread( this );

           myThread.start();

          }

     }

 

/** Resize us to be wide enough for the status string. */

  public void init() {

    resize( 300, 100 );

  }

 

/** Stop this thread. */

  public void stop() {

       myThread = null;

     }

 

/** The main loop for the main thread. */

  public void run() {

           while( myThread != null )

                {

                try {

                Thread.sleep( 300 );

                     } catch( InterruptedException e )

            { System.out.println("exception" ); }

                showStatus( iteration+"th iteration " );

                repaint();

                iteration++;

                }

     }

/** Paint our window.  Destroy each applet we find,

then display a string boasting of this achievement.

*/

  public void paint( Graphics g ) {

       String gString;

     int i = 0;

 

       AppletContext ac = getAppletContext();

     Enumeration e = ac.getApplets();

       while( e.hasMoreElements()) {

             Applet a = (Applet)(e.nextElement());

           Point p = a.location();

          // The list will always include "this", so check it

             if( a instanceof ch4_fig9 )

                gString = new String( "this is us" );

          else {

        Container c = a.getParent();

        if( c == null ) // this applet was already removed

          continue;       

                // Now destroy the other applet

        a.stop();

        c.remove(a);

             gString = new String( "Stopped applet at "+p.x+","+p.y+" "+iteration);

                }

         g.drawString( gString, 10, 20*(i+3) );

 

             i++;

     }

 

  }

}

 

<!DOCTYPE HTML PUBLIC

"-//SQ//DTD HTML 2.0 HoTMetaL + extensions//EN">

<HTML><HEAD><TITLE>The Killer Applet</TITLE>

</HEAD>

<BODY>

<P>

The applets below spend all their time fighting over access to

the browser status line, and trying to write strings into each

other presentation space.

</P>

<applet code=chap4/ch4_fig8a.class width=100 height=100>

<param name=Name value=Applet_A>

</applet>

<applet code=chap4/ch4_fig8a.class width=200 height=100>

<param name=Name value=Applet_B>

</applet>

<applet code=chap4/ch4_fig8a.class width=300 height=100>

<param name=Name value=Applet_C>

</applet>

<applet code=chap4/ch4_fig9.class width=400 height=100>

</applet>

</BODY></HTML>

Listing 4-9: the killer applet.

Try running this applet and you’ll see that only the killer applet remains running.  Notice also that, not only do the applets stop running, but their screen output disappears.  While we’ll talk more about removing Components in the next section, it’s important to note that even though we’ve ‘stopped’ the other applets via the stop method, those applets continue to exist, and the AppletContext still returns them every time we call getApplets.  That’s why we check the return from getParent.  Calling Container.remove on a Component means that the next call to getParent will return a null.  That’s how we know that the Component is no longer displayed.

Dealing With Stopped Applets

What to do with stopped applets is a more interesting question than it might seem.  In Listing 4-8 we added a method, mouseDown, that stops the applet if the user clicks on it. 

     public boolean mouseDown( Event e, int x, int y ) {

           stop();

           return( false );

     }

Listing 4-10: the mouseDown method.

Run listing 4-8 under Netscape and try clicking on one of the applets.  What you see is that the applet does stop running, but its last ‘state’ remains on the screen.  In the AppletContext’s (browser’s) view of the world, the ‘stopped’ applet still exists, and thus his section of the browser window should remain uncleared, and unused.  Try stopping one of the applets, then moving the browser window around.  What you’ll see is that the applet hasn’t really gone away at all.  All that’s happened is that the run method has returned.  When the applet window gets covered and uncovered, Java decides that all the applets need to be repainted and it calls Applet.paint for all the applets on the screen.  What we need to do is remove the applet from the list of ‘things’ that get repainted.  To do this, we add a single line to Applet.stop as shown in Listing 4-11.

package chap4;

 

import java.util.*;

import java.io.*;

import java.net.*;

import java.applet.*;

import java.awt.*;

 

/** An applet that finds the other applets that exist within

this AppletContext, fights with them to write a string to the

browser status line, and writes a string directly into the

other applet windows.  Modified to stop and 'disappear' from

the screen if the user clicks on the applet.

 

@version 1.10, 11/20/95

@author John Rodley

*/

 

public class ch4_fig11 extends Applet implements Runnable {

 

 

/** The main thread */

Thread myThread = null;

String TheString = null;

String myName;

String paintString = null;

int iteration = 0;

int paintIteration = 0;

 

/** Start the main thread for this game. */

  public void start() {

       TheString = new String( "Our applet is on screen now in " );

     if( myThread == null )

          {

             myThread = new Thread( this );

           myThread.start();

          }

     }

 

 

/** Stop this thread. */

  public void stop() {

       myThread = null;

     }

 

/** The main loop for the main thread. */

  public void run() {

           while( myThread != null )

                {

                try {

                Thread.sleep( 300 );

                     } catch( InterruptedException e )

            { System.out.println("exception" ); }

                showStatus( myName+" "+TheString+iteration+

                      "th iteration "+paintString );

                repaint();

                iteration++;

                }

     }

 

/** Paint our window.  Display a string in this window for

each applet we find, then display a string in each of the other

applets windows.

*/

public void paint( Graphics g ) {

     String gString;

     int i = 0;

 

     // Label this applet with its name

     g.drawString( "Name: "+myName, 0, 20 );

 

     AppletContext ac = getAppletContext();

     Enumeration e = ac.getApplets();

     Dimension d = size();

     g.drawRect( 0, 0, d.width-5, d.height-5 );

     while( e.hasMoreElements()) {

           Applet a = (Applet)(e.nextElement());

           // If this applet is a ch4_fig8 then it has a name parameter set

           String s = a.getParameter("Name");

           if( s == null )

                s = new String( "not a ch4_fig8 applet" );

           Point p = a.location();

           gString = new String( "Applet at "+p.x+","+p.y+" "+s );

           // The list will always include "this", so check it

           if( a == this )

                gString = new String( gString+" this is us" );

           else {

                // Now draw a string into the other applets window

                Graphics g1 = a.getGraphics();

      // If this applet's already stopped, g1 will be null

      if( g1 != null )

                g1.drawString( myName+" calling ...", 0, 40 );

                }

           // draw the string describing the other applet in our window

           g.drawString( gString, 10, 20*(i+3) );

    

           i++;

           }

     }

 

/** If the user clicks on the applet, stop it.

@return false

*/

  public boolean mouseDown(Event e, int x, int y) {

     stop();

    getParent().remove(this);

       return( false );

  }

 

/** Initialize the applet. Resize it and give it a name.  Make

sure that the applet is 'named' via the applet tag.

*/

  public void init() {

     myName = getParameter( "Name" );

       if( myName == null )

             myName = new String( "Set the Name= parameter please!" );

     resize( 210, 210 );

     }

}

 

 

Listing 4-11: Listing 4-8 modified to clear the applets window.

This clears the space that our Applet once occupied, and removes it from the list of Components, ensuring that it never gets repainted.  Removing the applet Component from the Container it resides in results in an applet that has no screen representation.  This means that ANY call to that applet’s Component superclass, such as the paint methods call to a.getGraphics, will probably fail.  We take this into account in paint by adding a null return check to a.getGraphics.

In truth, you can’t really get rid of an applet.  There’s no delete operator in Java,  so you can’t just destroy it, and the garbage collector won’t get rid of it automatically until there are no more references to it.  The applet was created (via the new operation) by the browser, and the applet itself doesn’t have write access to the browsers list of applets, so there will always be that one reference to the applet.  The best we can do, for now, is to remove the applet Component from the browsers list of Components, and hope that the browser (whichever one it is) will re-use the now-available screen space.  We’ll talk more about Components and Containers in Chapter 5.

Applets and Hyperlinks

We can combine our persistent applet with the AppletContext to implement a rudimentary web trolling applet.  This applet takes a list of URLs, and instructs the AppletContext to visit those URLs, in sequence, with a delay of 30 seconds at each document. 

package chap4;

 

import java.util.*;

import java.io.*;

import java.net.*;

import java.applet.*;

import java.awt.*;

 

/* A threaded applet that takes a list of URLs as an argument,

then visits each of those URLs, delaying for delay milliseconds

between URL switches.

 

@version 1.0 12/23/95

@author John Rodley

 */

 

public class ch4_fig12 extends Applet implements Runnable {

 

/** The main thread */

Thread myThread = null;

int iteration = 0;

Vector docURL;

int delay = 10000;

 

/** Start the main thread for this game. */

public void start() {

     if( myThread == null )

           {

           myThread = new Thread( this );

     myThread.start();

           }

     }

 

 

/** Stop this thread. */

public void stop() {

  myThread = null;

     }

 

/** The main loop for the main thread. */

public void run() {

     System.out.println( "Runnable.run" );

     iteration = 0;

           while( myThread != null )

                {

                AppletContext ac = getAppletContext();

                showStatus( "Switching to "+docURL.elementAt(iteration));

                ac.showDocument((URL) docURL.elementAt(iteration ));

                try {

                Thread.sleep( delay );

                     } catch( InterruptedException e ) { System.out.println("exception" ); }

                iteration++;

                if( iteration >= docURL.size() )

                     iteration = 0;

                }

           myThread = null;

     }

 

/** Initialize the applet. Resize and load images.

*/

public void init() {

     docURL = new Vector(1);

     int i = 1;

     String sDelay = getParameter("Delay");

     if( sDelay != null )

           delay = new Integer( sDelay ).intValue();

 

     while( true ) {

           String s = getParameter("Document"+i);

           if( s == null ) break;

           i++;

           try {

           docURL.addElement( new URL(s));

                } catch( Exception e) { break; }

           }

     resize( 210, 210 );

     }

}

 

Listing 4-12: a Web crawling applet.

 

One serious caveat to this applet: the documentation for AppletContext describes showDocument as an optional method - browsers are NOT required to implement it. Different browsers implement in different ways, and some don’t implement it at all.  Their reasoning, is easy to divine.  If you could jump around to various links via Java applets, using the browser simply as a base, then the usefulness of the browser’s fancy interface is called into question.  In fact, the whole design of showDocument smells of the kind of corporate politics that often makes programming an exercise in nonsense-control.  Why, if showDocument’s behaviour is undefined, doesn’t it at least return a value (instead of a void) or throw an exception to tell you whether it’s going to do what you requested? 

In Chapter 7, we’ll take a more hands-on approach to net.trolling, by recursively fetching HTML documents ourselves and searching them for patterns.

Reloading Applets

Most browsers support the notion of ‘reloading’ an HTML page - clearing the browser window, rereading the HTML source from the http server and redisplaying it in the browser window.  This is useful, or example, where pages have cgi scripts embedded (i.e. hit counting) and the page needs to be downloaded for the script to run or when a page currently in local cache is known to have changed back on the server.  Unfortunately, there is no hard and fast rule as  to just what ‘reloading’ an applet means.  Going strictly by the HTML example, what you might expect is that the applet gets stopped, and ‘unloaded’, whatever that means, re-read from the Web server, reloaded and restarted.  The simple applet of listing 4-13, a modification of the singing applet, plays a different audio clip whenever init, start, stop or destroy gets called.  Try it under your favorite browser and see what happens.

package chap4;

 

import java.awt.Graphics;

import java.applet.Applet;

import java.applet.AudioClip;

import java.net.*;

 

/** A simple app that plays tunes at the four different stops

along the applet execution trail - init, start, stop, and

destroy.  The parameters?

  StartClip

  StopClip

  DestroyClip

  InitClip

 

@version 1.0 12/20/1995

@author John Rodley

*/

public class ch4_fig13 extends Applet {

  String sAppletInfo;

  URL StartClipURL;

  URL StopClipURL;

  URL InitClipURL;

  URL DestroyClipURL;

 

  String pinfo[][] = {

    {"InitClip", "URL",

          "The URL of an audio clip played at init" },

    {"StartClip", "URL",

          "The URL of an audio clip played at start" },

    {"StopClip", "URL", 

          "The URL of an audio clip played at stop" },

    {"DestroyClip", "URL",

          "The URL of an audio clip played at destroy" }

     };

 

/** Whenever a user visits our page, the browser calls this.

This is an override of an Applet method.. */

  public void start() {

    if( StartClipURL == null )

      return;

    play( StartClipURL );

  }

 

/** Whenever the user leaves our page, this gets called.

Another override of an Applet method.

*/

  public void stop() {

    if( StopClipURL == null )

      return;

    play( StopClipURL );

  }

 

/** Tell anyone who might inquire what we are.

@return A string describing this applet.

*/

  public String getAppletInfo() {

    return("JRodley - Java Applets Book - Chapter 4 figure 13");

    }

 

/** Tell anyone who might inquire how to use this applet.

@return A 2d string array describing the arguments to this

applet.

*/

  public String[][] getParameterInfo() {

    return pinfo;

    }

 

/** Play the destroy clip. */

  public void destroy() {

    if( DestroyClipURL != null )

      play( DestroyClipURL );

  }

 

/** Connect to the URLs specified by the StartClip and EndClip

parameters in the applet tag.

*/ 

  public void init() {

    try {

      InitClipURL = new URL(getParameter("InitClip"));

      StartClipURL = new URL(getParameter("StartClip"));

      StopClipURL = new URL(getParameter("StopClip"));

      DestroyClipURL = new URL(getParameter("DestroyClip"));

      } catch( MalformedURLException e )

          { System.out.println("exception"+e );}

    if( InitClipURL != null )

      play( InitClipURL );

  }

}

 

Listing 4-13: Testing the reload button.

For single-threaded applets, this is all very simple, but it gets much more complicated when applets go creating their own threads.  It is very difficult, perhaps impossible, for a browser to shut down independent applet threads in an orderly fashion, mostly because there’s no way to know what those threads are doing.  Only the applet that created them knows that.  Thus, it is incumbent upon the applet to shut down its own threads in stop and/or destroy.

Conclusion

Here, we’ve seen what applets look like, where they appear, and how they behave.  We’ve shown how the interaction between the browser and its applets is strictly defined by the Applet class and the AppletContext interface.  We’ve also discussed the methods available to an applet, the flow of execution, applet persistence, and the environment in which applets execute.  And finally, with the web trolling applet, we’ve seen a little glimpse of how applets can enhance the functionality of browsers to the point of turning them into little more than Java platforms.


Exercises:

1. Name three things you can do via the Netscape user interface that you can’t do with a Java applet running under Netscape.

2. Name three things you can do with a Java applet that you can’t do via the Netscape user interface.

3. Are the following sequences of Applet method invocations possible?  Why not?  Explain what the user (or the browser) is doing to cause each of these sequences.

a).  Applet.init

     Applet.start

     Applet.stop

     Applet.destroy

b).  Applet.init

     Applet.destroy

c).  Applet.init

     Applet.start

     Applet.stop

     Applet.start

     Applet.stop

     Applet.destroy

d)   Applet.start

     Applet.init

     Applet.destroy

     Applet.stop

e)   Applet.init

     Applet.start

     Applet.destroy

 

4. What happens to the applet in Listing 4-8 when Applet.stop is called?  Does it continue to exist?  Will it’s paint method be called if the browser is minimized, then restored?

5. What method do you need to call to remove an applet from the Enumeration returned by AppletContext.getApplets?

6. What happens if you call getParameter on a parameter that has NOT been set in the applet tag?

7. Do you need to explicitly reference an AudioClip object to play audio in an applet?