Find out how to use this generic, flexible design for deploying robust, multi-screen Java applets over the Internet, intranets, and extranets
Java can be used to develop applets, standalone applications (GUI and console), and packages (library of reusable classes). Unlike applications, however, applets are typically confined to Web browsers and therefore are somewhat limited in their user interface design. To get around this limitation, I will provide you with a generic, flexible and well-thought-out design for Java applets, giving you a head start into deploying robust, multi-screen applets over the Internet, intranets, and extranets.
An applet primer
Generally speaking, applets are embedded in HTML pages using the
Interface obstacles
The standard applet user interface works fine for mini applets that can function within one given applet screen. However, this one-screen-per-HTML-page method does not scale well for organizations that need to deliver robust programs to their target audience over the Internet, intranet, or extranets.
There are some alternatives to this standard interface. You can launch windows in their own frames, which provides a way for applets to contain more than one window at one time. Another alternative is to use dialog boxes for additional screens. Although these alternatives do provide solutions to the standard interface, many users find them annoying. I continually hear gripes about Web sites that clutter desktops with additional browser windows.
So, what do you do if your applet requires multiple screens, but having separate windows is not a viable option? The answer: Use a design that works in the confinement of a single HTML page, but allows multiple screens in a single applet embedded in that HTML page.
Desperately seeking solutions: the Multi-Panel approach
The Multi-Panel Applet Design (MPAD), which allows multiple panels, or screens, to be incorporated into a single applet, is perfect for applets but can just as easily be applied to standalone GUI applications. MPAD is not a new Java API or technology; rather, it is a design that provides some significant benefits:
- Developers can create individual panels independently and integrate them into the applet at a later point
- Individual panels can be efficiently unit-tested by their developers
- Data can be exchanged between panels in an easy, extensible, and extremely flexible fashion
- The appletโs functionality can be enhanced using JavaScript
MPAD uses a Panel Manager to control individual panels. Each panel consists of GUI controls/widgets and performs a specific task. For example, a login panel might contain TextField objects to obtain an ID and password from a user and perform login authentication for a given system. The panels are chained together in a sequence, with the last panel in sequence visible in the appletโs display area. This mechanism is similar to a stack where you can โpushโ (and โpopโ) objects onto the stack, but can only โpeekโ at the object on top of the stack. Note:Although MPAD allows you to see only one screen at a time, you can easily overcome this limitation by extending the design.
Although the Panel Manager itself is the main applet class, it works behind the scenes to display individual panels, which are the visible components in this design. What makes MPAD extremely open and flexible is that the Panel Manager has no knowledge of these panels at startup because their names are not hard-coded in the Panel Manager code; rather, they are provided to the Panel Manager at run time.
MPAD design specs: An overview
Each panel in the Multi-Panel Applet Design has a one-to-one relationship with a Java class file. In other words, the Java source code for each panel is in its own Java source file. The Panel Manager uses the class name to create an instance of the panel and display it. The initial panelโs class name is extracted from the appletโs parameters, and all subsequent panel class names are provided to the Panel Manager via events from the individual panels. Therefore, the only hard-coding required is in the individual panels, which is the class name of the next panel to be displayed. For example, a login panel would contain the name of the panel that should display following a successful login.
Now that you have a basic understanding of the design, letโs take a closer look at the individual pieces. But before we begin, you may wish to browse through the live demo of the applet below so the remainder of the article will be easier to follow (a similar demo also is available at the end of the article). The demo contains three panels: a Login panel, a Record List panel, and an Add New Record panel. On the Login panel, enter any user ID and click on the Login button. On the Record List panel, click on any of the image buttons (except the last two) to go to the Add New Record panel. On the Add New Record panel, click on OK to add a record, or Cancel to abort.
Note: If you are using Netscape Navigator via a proxy server, you might encounter problems running the demo. If this is the case, download the archive file found in the Resources section of this article and view the demo locally.
A closer look at the Panel
Manager
Note: Because most browsers do not yet support JDK 1.1, I have decided to present the MPAD design using the JDK 1.0.2 event model. However, the downloadable archive file found in the Resources section of this article provides source code using both the 1.0.2 and 1.1 versions of the event model.
As I mentioned earlier, the Panel Manager doesnโt have any hard-coded values, which results in completely generic source code. The Panel Manager performs the following functions, which are illustrated in the figure Interaction between the Panel Manager and individual panels:
- Builds a
java.util.Propertiesobject from a configuration file (weโll discuss this objectโs significance later in the article). Instantiates the first class and displays it. This step includes the following tasks:
- Obtaining the first classโs name from the applet parameters (discussed later in this article)
- Creating an instance of the panel class using
java.lang.Class - Displaying the panel using
java.awt.CardLayout - Adding the panel to an internal list (
java.util.Vector) of panel references
- Responds to the following events, sent by the panel classes:
- ADD, which creates and displays a new panel. When this event is received, all the steps described for the first class are executed, except the class name is extracted from the
java.awt.Eventobject instead of an applet parameter - REMOVE, which removes the specified panel from the display area and also from the internal list of panel object references
- ADD, which creates and displays a new panel. When this event is received, all the steps described for the first class are executed, except the class name is extracted from the
Listing 1 provides the complete source code for the Panel Manager. As you peruse the code, youโll notice a few peculiar things that require a bit of explanation.
The first unusual thing youโll encounter is the following snippet, which builds a java.util.Properties object:
properties=new Properties();
InputStream is = (new URL(getCodeBase(), "JPanels.cfg")).openStream();
properties.load(is);
is.close();
properties.put("Applet", this);
properties.put("NotificationComponent", this);
This is how the applet gets its parameters, not via the typical java.applet.Applet.getParameter() method. In addition, this is how data between panels is exchanged. Weโll discuss this style of parameter passing and its benefits later in the article.
The next peculiar thing is that the handleEvent method only handles two events โ one to ADD a new panel and the other to REMOVE an existing panel. If necessary, you can easily extend MPAD by adding new event types (REFRESH, SHOW_PANEL, for example).
public boolean handleEvent(Event evt)
{
switch(evt.id)
{
case ADD:
panelAdd(evt.arg);
.
.
case REMOVE:
panelRemove(evt.arg);
Another stumbling block is the panelAdd() method. For starters, notice how a new instance of a panel class is created with the java.lang.Class object. The instance is cast to JPanel, a class that all panel classes must extend in order for this design to work. Weโll discuss JPanel later on.
Class c = Class.forName((String)arg);
jp = (JPanel)c.newInstance();
Next, notice some of the initial steps that occur when a new panel is created, such as passing the global Properties object to the new panel so it has access to the appletโs parameters. Also, notice the calls to the init(), start(), and stop() methods; these methods are defined in the JPanel class but can be overridden by the panel subclasses to perform such tasks as setting the user interface and allocating/deallocating objects. This style of using special methods simulates how applets work.
jp.setProperties(properties);
jp.init();
jp.start();
.
.
((JPanel)vPanels.lastElement()).stop();
Another unusual portion of code is shown next. This snippet adds the new panel reference to the internal java.util.Vector and to the global Properties object, adds the panel to CardLayout layout manager, and shows the new panel.
vPanels.addElement(jp);
add(jp.getClass().getName(), (Component)jp);
jCardLayout.last(this);
this.properties.put(jp.getClass().getName(), jp);
Finally, the panelRemove() method performs the tasks in the reverse order of the panelAdd(), which we just discussed.
Now that we have those explanations out of the way, click here to see the complete Panel Manager source code.
Developing individual panels
Developing individual panels is very similar to developing individual applets โ with one main difference: The panels must extend the
JPanel
class (see Listings 1 and 2) instead of extending the
java.applet.Applet
class. The figure
JPanel* class hierarchy
provides a graphical view of the class hierarchy.
Similar to applets, the panels can override special methods, such as init(), start(), stop(), and destroy(), for performing initialization and termination type functions. For example, the init() method is a good place to create new GUI objects and place them on the screen. The benefits of simulating applets are that the learning curve for developing panels is minimal for developers who understand applets; and the panels can easily be converted to individual applets if required.
Listing 1: JPanel.java
public class JPanel extends Panel
{
protected Component notifyComponent=null;
protected Applet applet=null;
protected Properties properties=null;
public void init() {}
public void start() {}
public void stop() {}
public void destroy() {}
public void setProperties(Properties p)
{
properties = p;
if (properties != null)
{
applet = (Applet) properties.get("Applet");
notifyComponent = (Component)properties.get("NotificationComponent");
}
}
}
Listing 2: JPanelRecordList.java (sample panel)
public class JPanelRecordList extends JPanel
{
.
.
public void init()
{
.
.
bpButtons.add(gib.build("images/1.gif"));
bpButtons.add(gib.build("images/2.gif"));
taAcctInfo = new TextArea(5, 40);
.
.
gbl.setConstraints(taAcctInfo, gbc);
add(taAcctInfo);
}
public void start()
{
lStatusBar.setText("");
}
public boolean action(Event evt, Object arg)
{
if (evt.target instanceof ImageButton)
{
if (evt.target == ibExit)
return notifyComponent.postEvent(new Event(this, JPanelManager.REMOVE, this));
else
if (evt.target == ibHelp)
{
try { applet.getAppletContext().showDocument(new URL(applet.getCodeBase(), "help/RecordList.html"), "helpWin"); }
catch (Exception e) { e.printStackTrace(); }
}
else
{
lStatusBar.setText("Please wait...");
return notifyComponent.postEvent(new Event(this, JPanelManager.ADD, "JPanelCreateRecord"));
}
return true;
}
return false;
}
.
.
Integrating panels
Integrating panels requires interaction between the panels and the Panel Manager. As I mentioned earlier, the first class name is obtained from an applet parameter, and the panel class is subsequently created and displayed. From then on, new panels are created by existing panels sending an event to the Panel Manager, as shown here:
return notifyComponent.postEvent(new Event(this, JPanelManager.ADD, "JPanelRecordList"));
To remove a panel, the panel in question must ask the Panel Manager to remove it from the sequence of panels by sending an event like this:
return notifyComponent.postEvent(new Event(this, JPanelManager.REMOVE, this));
The variable notifyComponent points to the Panel Manager object. The variable is loaded into the global Properties object by the Panel Manager during startup and is then set locally by JPanel each time the Panel Manager creates a new panel.
Now that we have seen how the Panel Manager works and how individual panels interact with it, letโs look at how data is provided to the panels.
Providing data to panels
The data in MPAD can be separated into two categories:
- Applet parameters
- Panel-to-panel data
Although the data is separated into two categories, both are stored in the same java.util.Properties object, which holds data as a series of key=value pairs, as shown in the figure Global properties object. The difference is in how the data (properties) gets into this object.
Youโll recall from our earlier discussion that the Panel Manager loads a configuration file into the global Properties object at startup. This process constructs the Properties object with initial values (applet parameters). A properties file looks something like this:
##############################################
# File: JPanels.ini
# Purpose: Configuration file for JPanels demo
##############################################
FirstClassName=JPanelLogin
toolbarIconWidth=20
toolbarIconHeight=19
After the initial Properties object is constructed, it is passed to each newly created panel via the JPanel.setProperties() method, which provides each panel with complete access to the object. This setup allows each panel to read from, add data to, and remove data from Properties. Although the data from the configuration files is loaded as java.lang.String, the values later added by panels can be any object type (for example, java.lang.Integer, java.applet.Applet, JPanel). For example, the Panel Manager stores the JPanel object reference as the value for each panel and uses their class names as the key, which allows other panels to send events to each other, if necessary.
The following code snippet demonstrates how properties can be โputโ into and โgottenโ from a java.util.Properties object:
properties.put("User", "anil");
notifyComponent = (Component)properties.get("NotificationComponent");
Unit-testing individual panels
I mentioned earlier that the Panel Manager loads the first panel class using an applet parameter, namely FirstClassName. Typically, this will be set to the first panel of your applet. However, for unit testing, you can set the first panel to whichever class name you want to point directly to. For example, in our applet demo, FirstClassName points to JPanelLogin, which leads to JPanelRecordList. But we could easily set FirstClassName to go directly to JPanelRecordList if we wanted to test it without having to go through JPanelLogin each time.
Additionally, if the panel you are unit testing expects data from other panels via the global Properties object, you can easily put test values in the configuration file instead of hard-coding them in your panel source files.
Enhancing an appletโs functionality via the browser
Everything I have covered in this article so far deals strictly with the Java language and its core API. However, you can greatly enhance the functionality of an applet by using some of the features available via the browser. These features include scripting with JavaScript and communicating with the browser to launch new windows for displaying external documents.
You can use JavaScript as a โwrapperโ around applets to enhance their functionality (in much the same way you use Unix shell scripts and MS-DOS batch files with executable programs). Although JavaScript provides a number of interesting and useful features, weโll discuss only the one relevant to this design.
You can use JavaScript to launch your applet in a separate window with or without a menu bar, tool bar, status bar, or other items. By launching your applet in a separate window, you give your applet the look and feel of a standalone application, as illustrated in the figure Using JavaScript to launch an applet in a separate window. In addition, the applet window will continue to function if the user leaves the page the applet was launched from, or even if the user closes the original browser window.
Now that you understand the concept, letโs look at a real script. The following JavaScript launches our applet in a separate window; to see a live demo, click on the button:
<SCRIPT LANGUAGE="JavaScript">
function launch()
{
window.open('applet.html', 'JBanker', 'toolbar=no,directories=no,menubar=no,status=yes,width=540,height=340,resizable=1');
}
</SCRIPT>
</HEAD>
.
.
<FORM><INPUT TYPE=BUTTON onClick="window.launch()" VALUE="Click here to launch demo"></FORM>
You also can launch a new browser window from within the applet by using the java.applet.AppletContext.showDocument() method, as shown in the figure Asking the browser to display a URL in a separate window. This technique is handy for displaying online help files (presumably developed in HTML, text, or another browser-supported format) or simply for linking to other URLs.
The following code launches a file called Help.html in a window named HelpWin (for a demo, click on the help button on any of the panels in the applet demo we ran early on in the article):
getAppletContext().showDocument(new URL(getCodeBase(), "Help.html"), "HelpWin");
One tidbit you donโt want to miss
I can think of several other tricks and tips to show you in this article; however, in the interest of brevity, I will mention only one more. To speed up the downloading of an applet, you can bundle the appletโs class files in an uncompressed zip file for Netscape Navigator (using the ARCHIVE attribute) or a CAB file for Microsoft Internet Explorer (using the cabbase ), as shown here:
<APPLET CODE=JPanelManager.class WIDTH=520 HEIGHT=320 ARCHIVE=JPanelClasses.zip>
<PARAM NAME=cabbase VALUE=JPanelClasses.cab>
</APPLET>
JDK 1.1 includes the Java Archive (JAR) file format, which standardizes the format of compressed files. Hopefully all browsers will support this format in the near future.
Conclusion
Hopefully you have a good idea of how MPAD works. Of course, to keep things simple, I have provided only the framework for my design along with a stripped down version of the source code. You can do a lot to enhance the design. Here are a few recommendations:
- Develop separate classes for loading/reloading the configuration file and launching the online help
- Develop data structures to hold panel event data
- Add new event types to the existing ADD and REMOVE events
If you come up with new or better ideas, be sure to send me an e-mail. Best of luck!


