Microsoft & .NET .NET MultiApplet: Running Multiple Applets Inside an Applet Window

MultiApplet: Running Multiple Applets Inside an Applet Window


What, exactly, is an applet? A very rough definition might be “a Java program that runs inside a browser”. However, there are definite differences between an applet and an unadorned, command-line Java application. Applets are inherently graphical; they are started from their

init()
method rather than their

main()
method; and so on.

In this article, we’ll be creating an applet that contains other applets running inside of it. In doing so, we’ll be exploring, in detail, the context in which an applet runs.

Practically speaking, it’s not really interesting to simply run several applets inside one applet; after all, it’s easier to just create a single Web page with several applets running within it. However, the techniques described here can be used to implement something more sophisticated—you could build an applet browser on top of the simple

AppletViewer
supplied with your Java Development Kit, or a custom in-browser applet debugger. In any case, you’ll learn more about how applets work, and how they interact with their environments.

Overview

We’ll be creating a class called

MultiApplet
. A multiapplet is an applet that contains other applets. The individual applets run as if they were being run normally, directly inside a browser window. However, they will be running in the artificial context of our multiapplet.

We’ll demonstrate the multiapplet by creating a simple test example, one that simply loads several instances of an applet and displays them simultaneously in a single applet window.

In our examples, we’ll be creating a multiapplet called

MultiAppletTest
, and we’ll be stuffing several instances of

SimpleApplet
into it.

SimpleApplet
is a trivial applet that displays a graphical pattern that can be manipulated by the mouse.

Bringing a subapplet into the multiapplet is a two-step process:

  1. Create an instance of the subapplet
  2. Place it within the multiapplet window

We’ll make the first step easy. To instantiate a new instance of SimpleApplet, you call

MultiApplet.addApplet()
:

Applet applet = addApplet( “SimpleApplet” );

After doing this, we have a new instance of our subapplet, but we can’t see it until we display it in the window. The most sensible thing to do now is to stick it into the main window, using a layout manager. We’ll use

GridLayoutManager
, as follows:

setLayout( new GridLayout( 2, 2 ) );
Applet applet = addApplet( “SimpleApplet” );
add( “1”, applet );

Contexts and Stubs

So far, this all seems pretty easy — not really much more complicated than placing one abstract windowing toolkit component inside another AWT component.

However, if you were to load a complicated applet into our multiapplet, you might find that it didn’t work the way it did on its own. It might not work at all. Clearly, there’s more to it than what we’ve already done.

An applet expects its context to supply a certain set of services. These services are defined through a pair of interfaces called

AppletContext
and

AppletStub
. (These interfaces are part of the

java.applet
package.)

Among other things, an

AppletContext
provides methods for loading images and audio clips across the Net from the applet’s site. It also allows you to instruct the browser to load a new Web page. An

AppletContext
can be thought of as a set of services that an applet developer might want to make use of when writing their code.

An

AppletStub
, among other things, provides a way to get the codebase and documentbase of the applet—these are the URLs pointing to the applet’s code, and the document containing the applet, respectively. It also provides access to the applet parameter tags that were specified in the HTML applet tag.

Both of these objects provide contextual services, but they are used in rather different ways. For one thing, an

AppletContext
is kind of a public thing—an applet can ask the system for the context using the

Applet.getAppletContext()
method. (In fact, any other class can ask for an applet’s context, too.) On the other hand, an AppletStub is a hidden thing—you can’t actually get the stub object from the system. It’s like hidden glue used to connect the applet to the services in the browser or applet viewer.

In most applet programming, however, you don’t really need to think about either of these objects. In fact, it’s quite possible that you’ve done your share of applet programming without ever explicitly using either of these objects. Most of the services they provide are accessed via the applet itself; the code inside

Applet
takes care of calling either the

AppletContext
or

AppletStub
for you.

The Code

For example, here’s the implementation of

Applet.getCodeBase()
from

Applet.java
in the Java source:

public URL getCodeBase() {
return stub.getCodeBase();
}

See?

Applet
itself doesn’t know how to get the codebase—it has to ask the surrounding system, via the stub. Correspondingly, here’s the implementation of

Applet.showImage()
:

public Image getImage(URL url) {
return getAppletContext().getImage(url);
}

Note, finally, that the stub itself supplies the

AppletContext
. Again, from

Applet.java
:

public AppletContext getAppletContext() {
return stub.getAppletContext();
}

In creating a multiapplet, we’re going to have to create an artificial context that will provide all of these services. This isn’t nearly as grueling as it sounds, however, since the multiapplet, itself, has been provided these very services by the browser. That is, while the subapplets are running inside our multiapplet, the multiapplet is running inside a proper context supplied by the browser. This context can be used to provide contexts for the subapplets. In most cases, it’s as simple as forwarding a request from the subapplet to the

AppletContext
or

AppletStub
belonging to the multiapplet.

Remember that some services are provided by the stub, and some by the context. To deal with the stub services, we’ll create an object called

MultiAppletStub
. A

MultiAppletStub
contains a reference to our multiapplet, so that it can forward requests to it.

The

MultiAppletStub
object implements the many methods of the

AppletStub
interface, but, in most cases, it just forwards the call to the applet:

public URL getDocumentBase() {
return applet.getDocumentBase();
}

The chaining of the method calls here is kind of tricky. Let’s say our subapplet calls

getDocumentBase()
. Here’s what happens:


  1. subapplet.getDocumentBase()
    calls

    MultiAppletStub.getDocumentBase()


  2. MultiAppletStub.getDocumentBase()
    calls

    MultiApplet.getDocumentBase()


  3. MultiApplet.getDocumentBase()
    calls

    [MultiApplet’s stub].getDocumentBase()

It’s hairy, but it works. Steps 1 and 3 are calls made by code within

Applet.java
; step 2 is implemented by the code fragment within MultiAppletStub (shown above). Step 2 is the little trick we use to make our real context serve as a kind of virtual context for the subapplets.

It’s easier to implement the forwarding of requests to

AppletContext
. In this case, we can actually get our hands on the

AppletContext
object supplied to our multiapplet, so we just let the subapplets use that same object by having our

AppletStub
provide that object. Thus, we don’t have to create a class called

MultiAppletContext
.

Here’s the implementation of

MultiAppletStub.getAppletContext()
:

public AppletContext getAppletContext() {
return applet.getAppletContext();
}

Here’s the chaining that goes on for a call to

getImage()
:


  1. subapplet.getImage()
    calls

    subapplet.getAppletContext()


  2. subapplet.getAppletContext()
    calls

    MultiAppletStub.getContext()


  3. MultiAppletStub.getContext()
    calls

    MultiApplet.getContext()


  4. MultiApplet.getContext()
    returns a browser-supplied context object


  5. subapplet.getImage()
    calls

    [context
    object].getImage()

That’s even hairier, but at least we didn’t have to create a

MultiAppletContext
class full of forwarding methods. And all we had to do was implement

MultiAppletStub.getAppletContext()
.

Handling Many Subapplets

MultiApplet
maintains a list of applets that have been loaded. Here’s the implementation of

addApplet()
:

public void addApplet( Applet applet ) {
// Create a new, “artificial” stub for the subapplet
AppletStub stub = new MultiAppletStub( this );
// Give it to the subapplet to use
applet.setStub( stub );
// Start up the subapplet
applet.init();
// Finally, store it in our list
applets.addElement( applet );
}

Note that this version of

addApplet
takes an

Applet
object as an argument. But we’ll prefer to use the one that just takes a name:

public Applet addApplet( String className )
throws ClassNotFoundException, IllegalAccessException,
InstantiationException {
// Create an instance of the named applet
Class clas = Class.forName( className );
Applet applet = (Applet)clas.newInstance();
// Add it to the system
addApplet( applet );
// Return it, in case the caller wants to access the Applet
// object directly
return applet;
}

This method throws a whole bunch of exceptions related to the act of creating an instance of an object from its name; you’ll have to catch them when you use this method.

Other issues

Another important feature of an applet’s environment consists of the execution control methods:

init() start(), stop(), and
destroy()
.

Each of these methods is called for you, automatically, by the browser environment. (You can also call them yourself, but that’s not usually done.)

start()
and

stop()
are used to suspend and resume execution when a browser is directed away from the page containing the applet. Generally, this doesn’t cause the applet to be removed from the system, but merely suspended.

init()
is called once when the applet is first started, and

destroy()
is called once when the applet is about to be removed from the system (such as when the containing browser window is closed). Since many applets have important initialization/cleanup code in these methods, it’s essential that our multiapplet system attend to these functions.

In the case of

init()
, we make sure to call that as soon as the subapplet is loaded, from within

MultiApplet.addApplet()
.

For the others, we implement as follows:

public void start() {
for (Enumeration e=applets.elements(); e.hasMoreElements();) {
((Applet)e.nextElement()).start();
}
}

That is, when the browser asks our multiapplet to start, the multiapplet, in turn, asks all the subapplets to start. The same goes for

stop()
and

destroy()
.

The Test Program

Our test program is an applet called

MultiAppletTest
that contains four instances of

SimpleApplet
. Here’s how it goes:

public void init() {
try {
// Set up a layout that allows us to see all the subapplets
setLayout( new GridLayout( 2, 2 ) );

Applet applet;

// Create four subapplets, and put them on-screen
applet = addApplet( “SimpleApplet” );
add( “1”, applet );
applet = addApplet( “SimpleApplet” );
add( “2”, applet );
applet = addApplet( “SimpleApplet” );
add( “3”, applet );
applet = addApplet( “SimpleApplet” );
add( “4”, applet );

// There are several exceptions thrown by addApplet(); we’ll
// just do the easy thing and catch everything here:
} catch( Exception e ) {
System.out.println( e );
}
}

This code just sets up a 2×2 grid of areas for the subapplets to run in. Each applet is loaded and placed in one of the four slots.

Trying Out the Code

First, check out
SimpleApplet
. Click and drag in the window to see the image change. Then, try out
MultiApplet
, to see several instances of

SimpleApplet
running inside a single applet window. You can play around with each of the applets separately.

Getting Wackier

Just for fun, let’s try running an instance of

MultiApplet
inside another instance of

MultiApplet
. This will lengthen the chain of calls used to implement the applet services, but we’ve taken care of that, and it should work correctly. It’s easy to do—instead of

applet = addApplet( “SimpleApplet” );
add( “2”, applet );

we do this:

applet = addApplet( “MultiAppletTest” );
add( “2”, applet );

This class is called

MetaMultiAppletTest
and you can try it out
here
.

Conclusion

By creating our artificial applet contexts and stubs, we’ve been able to provide a context for subapplets to work correctly in. We can be reasonably certain that most applets will work, unchanged, within our context (but see the next section for some possible problems with this).

Further Work

You might have noticed that

MultAppletStub.appletResize()
doesn’t forward itself to anywhere. We’ve just ignored it. We did this because it’s not clear how, in general, we should handle the sizing and layout of the subapplets. This depends crucially on how we lay the applets out. In our example, we used

GridLayout
, but this is by no means the best or only way to deal with this. There’s no straightforward solution to this problem, because it touches on the one way in which our “simulated context” isn’t identical to a real context: we are running as only one part of a larger applet window.

There are a few other things that could be improved to make it as easy as possible to move an applet from a real context to our simulated context. Applet parameters are a good example. Each applet can have its own set of parameters passed in from the HTML page. If we just stuffed all the subapplets’ parameters into a single multiapplet tag, we might find some name clashes between the parameter sets.

One solution for dealing with this would be to give each subapplet a name within the context of the multiapplet. Then, each parameter name could be prefixed by this subapplet name, as follows:

<param name=”foo” value=”20″>

becomes

<param name=”applet10-foo” value=”20″>

where

“applet10”
is the name we’ve assigned to one of our subapplets. This solves the name conflict problem.

To make this even better, we could automate the process of stripping the applet name from the parameter name. Our subapplet could call

getParameter<!– Changed getParamter to getParameter. -TM –>( “foo” )
, and

MultiAppletStub.getParameter
would look up the name of the requesting applet and return the value of

MultiApplet.getParameter<!– Changed getParamter to getParameter. -TM –>( “applet10-foo” )
. This would allow our subapplets to run unchanged—the subapplets wouldn’t have to know what their assigned names were.

Similar considerations could be contemplated for

getApplets()
.

In our example, we hardcoded the names and screen positions of each subapplet. There’s no reason that these couldn’t be specified as applet tags; then, you could have different instances of your multiapplet containing different subapplets without actually having to change the code.

One final thing to watch out for: avoid calling

init()
too early—if the multiapplet hasn’t actually been plugged into the system, then it may not have access to stubs or contexts, and it may not be able to load images or audio clips. The way we’ve set things up above, it’s fine, since everything starts from

MultiApplet.init()
, but if our multiapplet were created in a non-standard way (via

newInstance()
for example), then it might not have been properly initialized yet.

Source

MetaMultiAppletTest.java


// $Id$

import java.applet.*;
import java.awt.*;
import java.net.*;

public class MetaMultiAppletTest extends MultiApplet
{
public void init() {
try {
setLayout( new GridLayout( 2, 2 ) );
Applet applet = addApplet( “SimpleApplet” );
add( “1”, applet );
applet = addApplet( “MultiAppletTest” );
add( “2”, applet );
applet = addApplet( “MultiAppletTest” );
add( “3”, applet );
applet = addApplet( “SimpleApplet” );
add( “4”, applet );
} catch( Exception e ) {
System.out.println( e );
}
}
}

MultiApplet.java

// $Id$

import java.applet.*;
import java.net.*;
import java.util.*;

public class MultiApplet extends Applet
{
public Vector applets = new Vector();

public void addApplet( Applet applet ) {
// Create a new, “artificial” stub for the subapplet
AppletStub stub = new MultiAppletStub( this );
// Give it to the subapplet to use
applet.setStub( stub );
// Start up the subapplet
applet.init();
// Finally, store it in our list
applets.addElement( applet );
}

public Applet addApplet( String className )
throws ClassNotFoundException, IllegalAccessException,
InstantiationException {
// Create an instance of the named applet
Class clas = Class.forName( className );
Applet applet = (Applet)clas.newInstance();
// Add it to the system
addApplet( applet );
// Return it, in case the caller wants to access the Applet
// object directly
return applet;
}

public void start() {
for (Enumeration e=applets.elements(); e.hasMoreElements();) {
((Applet)e.nextElement()).start();
}
}

public void stop() {
for (Enumeration e=applets.elements(); e.hasMoreElements();) {
((Applet)e.nextElement()).stop();
}
}

public void destroy() {
for (Enumeration e=applets.elements(); e.hasMoreElements();) {
((Applet)e.nextElement()).destroy();
}
}

public void appletResize( int width, int height ) {
System.out.println( “ach” );
}
}

class MultiAppletStub implements AppletStub
{
private Applet applet;

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

public boolean isActive() {
return applet.isActive();
}

public URL getDocumentBase() {
return applet.getDocumentBase();
}

public URL getCodeBase() {
return applet.getCodeBase();
}

public String getParameter( String name ) {
return applet.getParameter( name );
}

public AppletContext getAppletContext() {
return applet.getAppletContext();
}

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

MultiAppletTest.java

// $Id$

import java.applet.*;
import java.awt.*;
import java.net.*;

public class MultiAppletTest extends MultiApplet
{
public void init() {
try {
// Set up a layout that allows us to see all the subapplets
setLayout( new GridLayout( 2, 2 ) );

Applet applet;

// Create four subapplets, and put them on-screen
applet = addApplet( “SimpleApplet” );
add( “1”, applet );
applet = addApplet( “SimpleApplet” );
add( “2”, applet );
applet = addApplet( “SimpleApplet” );
add( “3”, applet );
applet = addApplet( “SimpleApplet” );
add( “4”, applet );

// There are several exceptions thrown by addApplet(); we’ll
// just do the easy thing and catch everything here:
} catch( Exception e ) {
System.out.println( e );
}
}
}

SimpleApplet.java


// $Id$

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class SimpleApplet extends Applet
implements MouseListener, MouseMotionListener, Runnable
{
private int dpx, dpy;
static final private int spacing = 16;
private int spacing_offset = 0;
private int pause_ms;
static final private int pause_min=10, pause_max=200;
private Thread thread;
Random random = new Random( System.currentTimeMillis() );
private boolean yet_clicked=false;

public SimpleApplet() {
pause_ms = random.nextInt();
pause_ms = Math.abs( pause_ms );
pause_ms = pause_min + (pause_ms % (pause_max-pause_min));
}

public void init() {
System.out.println( “init “+this );
addMouseListener( this );
addMouseMotionListener( this );
}

public void start() {
System.out.println( “start “+this );
if (thread!=null) {
thread.stop();
thread=null;
}
thread = new Thread( this );
thread.start();
}

public void stop() {
System.out.println( “stop “+this );
thread.stop();
thread=null;
}

public void destroy() {
System.out.println( “destroy “+this );
thread.stop();
thread=null;
}

public void paint( Graphics g ) {
if (!yet_clicked) {
dpx = getSize().width/2;
dpy = getSize().height/2;
}
int width=getSize().width, height=getSize().height;
g.setColor( Color.black );
for (int i=spacing_offset; i

About the Author

Greg Travis is a freelance programmer living in New York City. His interest in computers can probably be traced back to the episode of “The Bionic Woman” where Jamie runs around trying to escape a building, whose lights and doors are controlled by an evil artificial intelligence. He’s a devout believer in the idea that when a computer program works, it’s a complete coincidence. He can be reached at [email protected].

Latest Posts

Related Stories