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() |
main() |
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 sophisticatedyou could build an applet browser on top of the simple
AppletViewer |
Overview
We’ll be creating a class called
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 |
SimpleApplet |
SimpleApplet |
Bringing a subapplet into the multiapplet is a two-step process:
- Create an instance of the subapplet
- 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 |
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 |
AppletStub |
java.applet |
Among other things, an
AppletContext |
AppletContext |
An
AppletStub |
Both of these objects provide contextual services, but they are used in rather different ways. For one thing, an
AppletContext |
Applet.getAppletContext() |
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 |
AppletContext |
AppletStub |
The Code
For example, here’s the implementation of
Applet.getCodeBase() |
Applet.java |
public URL getCodeBase() {
return stub.getCodeBase();
}
See?
Applet |
Applet.showImage() |
public Image getImage(URL url) {
return getAppletContext().getImage(url);
}
Note, finally, that the stub itself supplies the
AppletContext |
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 |
AppletStub |
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 |
MultiAppletStub |
The
MultiAppletStub |
AppletStub |
public URL getDocumentBase() {
return applet.getDocumentBase();
}
The chaining of the method calls here is kind of tricky. Let’s say our subapplet calls
getDocumentBase() |
-
callssubapplet.getDocumentBase()
MultiAppletStub.getDocumentBase()
-
callsMultiAppletStub.getDocumentBase()
MultiApplet.getDocumentBase()
-
callsMultiApplet.getDocumentBase()
[MultiApplet’s stub].getDocumentBase()
It’s hairy, but it works. Steps 1 and 3 are calls made by code within
Applet.java |
It’s easier to implement the forwarding of requests to
AppletContext |
AppletContext |
AppletStub |
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() |
-
callssubapplet.getImage()
subapplet.getAppletContext()
-
callssubapplet.getAppletContext()
MultiAppletStub.getContext()
-
callsMultiAppletStub.getContext()
MultiApplet.getContext()
-
returns a browser-supplied context objectMultiApplet.getContext()
-
callssubapplet.getImage()
[context
object].getImage()
That’s even hairier, but at least we didn’t have to create a
MultiAppletContext |
MultiAppletStub.getAppletContext() |
Handling Many Subapplets
MultiApplet |
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 |
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;
}
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 |
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() |
stop() |
init() |
destroy() |
In the case of
init() |
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() |
destroy() |
The Test Program
Our test program is an applet called
MultiAppletTest |
SimpleApplet |
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 |
Getting Wackier
Just for fun, let’s try running an instance of
MultiApplet |
MultiApplet |
applet = addApplet( “SimpleApplet” );
add( “2”, applet );
we do this:
applet = addApplet( “MultiAppletTest” );
add( “2”, applet );
This class is called
MetaMultiAppletTest |
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() |
GridLayout |
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” |
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” ) |
MultiAppletStub.getParameter |
MultiApplet.getParameter<!– Changed getParamter to getParameter. -TM –>( “applet10-foo” ) |
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() |
MultiApplet.init() |
newInstance() |
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; iAbout 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 mito@panix.com.