MPAD: A new design and development methodology for multi-panel applets

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 <APPLET> tag. When a user requests an HTML page, the browser parses the HTML text and upon detecting the <APPLET> tag, requests that the Web server send the class file pointed to by the CODE attribute; any other class files referenced by the initial class file also are downloaded as needed. Once the required class files download successfully, the browser runs the applet within the HTML page, using the WIDTH and HEIGHT specified in the <APPLET> tag as the applet's display area.

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.

You need a Java-enabled browser to view this applet.

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.Properties object 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.Event object 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
Panel Manager
Interaction between the Panel Manager and individual panels

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.

JPanel Class Hierarchy
JPanel* 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.

Properties
Global Properties 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.

JavaScript: Launching an applet window
Using JavaScript to launch an applet in a separate 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.

JavaScript: Launching a help window
Asking the browser to display a URL in a separate window

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 <PARAM>), 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!

Anil Hemrajani is the founder of Divya Inc. (www.divya.com, previously Chipsoft/chipsoftinc.com). Divya specializes in Java and Internet consulting, training, seminars, application development, e-mail/phone technical support, and off-the-shelf Java applets. His company recently has started a Java Jumpstart program, which helps companies new to Java get up to speed quickly, through a combination of training and consulting.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more