Chapter 16

Internet Foundation Classes


CONTENTS


While Microsoft Windows may have captivated the user community, many programmers were dismayed at the prospect of learning over 350 separate and disjointed function calls. Microsoft responded by encapsulating the Windows Application Programming Interface (API) in the Microsoft Foundation Classes, or MFC. These classes systematically organize the function calls that are included in the Windows API.

Sun's release of Java includes packages such as the Abstract Windowing Toolkit, which encapsulates a platform-independent graphical user interface. The AWT is a powerful tool, but many users feel a need for an even richer interface, without having to build it from the AWT and other low-level components. Netscape addressed this need by encapsulating the user interface, as well as other system capabilities, in the Netscape Internet Foundation Classes, or IFC.

Because the IFC is written in Java, you can use app-lications built with the IFC on any platform on which Navigator runs, including OS/2 Warp, the Macintosh, Windows, and the major variants of UNIX. Because Netscape offers LiveConnect, you can access the IFC-based applets from JavaScript and from plug-ins. Because IFC represents a large (and growing) body of functionality, you should use the IFC when writing your applets rather than developing the same functionality from lower-level components.

First Things First-Acquiring and Installing the IFC

To use the IFC, you need the Java Developer's Kit (JDK) version 1.0.2 or later. If you want to use LiveConnect, you also need the Netscape "glue" classes.

ON THE WEB
http://www.javasoft.com/nav/download/index.html  You can get the latest version of the JDK directly from Sun's JavaSoft site. Be sure to get the documentation and tutorial, as well-they're downloaded separately from this same page.

ON THE WEB
http://developer.netscape.com/library/ifc/index.html  Download the latest version of the IFC from Netscape's developer site. If you're a member of Netscape's developer program, DevEdge, check the members-only site to see if Netscape has an even newer version available.
This chapter was written with the IFC beta version called Barium. Barium is the last beta before the final shipment, so there should be few differences between the examples in this chapter and the finished product. Note, however, that the major IFC packages are named netscape_beta.* in the beta version. Look for the _beta to be dropped in the finished version.

Caution
Macintosh users take note: six of the class files in the Barium release are zipped as TEXT files. By default, Macintosh zip utilities like ZipIt will add linefeeds to these files, corrupting them. Take care to turn linefeeds off before extracting these files from the archive.
The files are ExtendedTarget.class, TextViewHTMLElements.class, Window.class, InternalWindowOwner.class, WindowOwner.class, and DragDestination.class, all in the netscape_beta/application folder. Figure 16.1 shows how these files look in ZipIt.

Figure 16.1: Make sure the linefeed option (LF) is turned off (rightmost column) so that none of the IFC files are corrupted.

Setting the CLASSPATH Variable

Once you have the IFC, use your browser to open installation.html. (You'll also want to look at ReleaseNotes.html before you get too deep into the IFC.) Most of the information in installation.html has to do with the environment variable CLASSPATH. Navigator interprets this environment variable as a list of directories in which to search for compiled Java classes. For convenience, set the CLASSPATH once in your AUTOEXEC.BAT file.

Tip
The Mac OS doesn't use environment variables, so Netscape has pre-programmed Navigator to look in a particular directory. The path given in installation.html is not quite correct (the path name uses a stylized f, ƒ, which cannot be reproduced in simple ASCII). Figure 16.2 shows the correct installation on the Macintosh.
You don't need to reboot the Macintosh after installing the IFC classes, but you should restart Navigator.

Figure 16.2: On the Macintosh, put class libraries like the IFC classes into the System folder, in Preferences, Netscape f, Java, netscape-classes.

ON THE WEB
http://devtools.apple.com/mrj/  While you're setting up your Macintosh Java environment, visit this Apple site and pick up the Mac OS Runtime for Java and the corresponding SDK. This software is based on Sun's Macintosh Java environment but contains numerous small improvements made by Apple.

For yet another Macintosh Java environment, visit http://www.microsoft.com/ie/mac/. This site describes the Macintosh version of Microsoft Internet Explorer, which comes with its own Java Virtual Machine (JVM). Microsoft developed this JVM in collaboration with Metrowerks, a leading vendor of Macintosh compilers. This JVM is interchangeable with the Apple-developed MJR-in fact, you can have both environments on your computer and switch from one to the other at runtime. From this site, you link to http://www.microsoft.com/ie/download/, where you select the product and initiate download.

Troubleshooting Your Installation

Once you've installed the IFC, use Navigator to open the example applet HelloWorld. The resulting screen should resemble Figure 16.3. If you don't get something similar, choose Options, Show Java Console to see the error messages.

Figure 16.3: If your installation is successful, you should be able to run the HelloWorld applet. Note that the Java Console shows no error messages.

Tip
If you're developing for Netscape ONE, chances are good that you use Netscape Navigator as your browser. You don't have to use Navigator, however, to look at an IFC-based applet. As long as CLASSPATH is set correctly, you can run these applets in any Java-aware browser, including Microsoft Internet Explorer or HotJava, or you can use AppletViewer (which comes with the JDK).

Most of the examples that come with Sun's JDK start with a class that extends Applet. For example, if you look at the Applets/Applets/SortDemo example, which comes with the JDK, you'll find that example1.html references the class file SortItem. SortItem.java (the source for SortItem.class) uses system classes (such as String), and user-defined classes (such as SortAlgorithm). Navigator first looks for the classes in the same directory as the HTML file, where it finds SortAlgorithm.class. Then it looks in the files specified by CLASSPATH (or, on the Mac, the pre-programmed directories in the System folder).

Note
Most of the problems that occur during installation occur because of a malformed CLASSPATH variable. If you're having problems, it's worth your while to learn as much as you can about CLASSPATH.
If the class is part of a package, Navigator looks for a directory whose name matches the package name, then looks for the class file inside that directory. If the package name has multiple components (for example, java.lang.*), Navigator treats each component as a directory level. Thus, to find the class file for netscape_beta.application.FoundationApplet, Navigator looks along the CLASSPATH directories for a directory named netscape_beta. Inside that directory, it looks for a directory named application. Finally, it looks for the file named FoundationApplet.class in the application directory. Refer back to Figure 16.2 to see how these directories are laid out on the Macintosh.

ON THE WEB
http://www.javasoft.com/nav/download/index.html  For even more information on the CLASSPATH variable, download the HTML version of the Java tutorial from this site, uncompress the tutorial file, and look at file://path-to-tutorial/tutorial/java/javaOO/createpkgs.html. This page gives full details about how your browser interprets CLASSPATH.
If you prefer to link directly to the FTP site, use ftp://ftp.javasoft.com/docs/tutorial.html.tar.Z or ftp://ftp.javasoft.com/docs/tutorial.html.zip. UNIX users should download the tar.Z version; use the UNIX compress utility to uncompress the archive; then use tar to extract the files themselves. Windows 95 and Windows NT users will feel more at home with the ZIPped version. If you download the ZIPped version, be sure you don't try to unZIP it with a utility that enforces the old MS-DOS naming convention (eight characters in the name, three characters in the file extension). Instead, use a newer utility like WinZIP95.
Macintosh users can unzip the zipped tutorial file with ZipIt, a Macintosh utility available at ftp://ftp.awa.com/pub/softlock/mac/products/zipit/, or you can down uncompress and untar the tar.Z version with MacCompress (from ftp://ftp.cdrom.com/pub/mac/umich/util/compression/maccompress3.2.hqx) and tar (available at ftp://ftp.cdrom.com/pub/mac/umich/util/compression/tar4.0b.sit.hqx).

ON THE WEB
http://www.awa.com/softlock/zipit/zipit.html  The ZIP format is most commonly used on MS-DOS and Windows machines, but Sun uses it as the archive format for class libraries. Learn more about the leading Macintosh version of the zip/unzip utility at this site.
On UNIX machines, you can use Zip from Info-Zip. Their Web site is http://quest.jpl.nasa.gov/Info-Zip/Info-Zip.html.

IFC applets are built differently than Sun's example applets. The HTML file specifies the applet's code as NetscapeApplet and passes the name of the class to load as a parameter to NetscapeApplet. For example, HelloWorld.html contains the lines

<applet code="NetscapeApplet" width=320 height=200>
    <param name="ApplicationClass" value="HelloWorld">
</applet>

NetscapeApplet.java, in turn, contains

import netscape_beta.application.*;
import netscape_beta.util.*;
public class NetscapeApplet extends FoundationApplet {
...

So, when Navigator opens HelloWorld.html, it looks in the HelloWorld directory for the file NetscapeApplet.class. Finding it, it looks for the file FoundationApplet.class, which is part of the netscape_beta.application package. If you get an error like

NetscapeApplet.java(4): Package netscape_beta.application not
found in import.

Navigator has not found the netscape_beta.application package in any directory specified by CLASSPATH. If you don't get that error, but you do get an error like

java.io.FileNotFoundException: \F:\projects\src\test\Simple\netscape_beta\
application\FoundationApplet.class

then Navigator found the package but couldn't find FoundationApplet.class. In either case, double-check CLASSPATH. If you're on a Windows machine, make sure you changed AUTOEXEC.BAT, saved the file, and then rebooted. If you're running under UNIX, you probably set the environment variable in your personal .profile. You should log out and log back in. On the Mac, be sure to quit out of Navigator and restart the browser.

Tip
If you're a Macintosh user, you won't be able to modify CLASSPATH, so Sun utilities, like the Java compiler and Java Runner, won't be able to find your Netscape classes in the System folder. The solution is to make an alias to the netscape_beta folder and put that alias in the same directory as your Java file.
To create this alias, select the netscape_beta folder (in the System folder, in Preferences, Netscape ƒ, Java, netscape-classes) with the Finder, and use File, Make Alias. Copy the alias file (named netscape_beta alias) to the folder in which you keep your Java files. Rename the alias netscape_beta by clicking the name and removing " alias" at the end. Figure 16.4 shows the finished result.

Figure 16.4: Use an alias to the Netscape class libraries so that the Java compiler and Java Runner work correctly on the Macintosh.

Tip
Compiling with the IFC requires more memory than compiling typical Java applets. If the compiler (or any other Java application) complains about running out of memory, free up more memory by exiting other applications. To build platform-specific structures, the compiler uses the memory that you explicitly allocate to it and uses the machine's remaining memory to build Java-specific structures. While the heap used for Java is supposed to have dynamic garbage collection, it's a good idea to not let your compiler run low on memory.
The Macintosh version of javac reports how much memory is available on that heap and how much of the heap is being used.

Frameworks and Classes

Object-oriented analysis, design, and programming have been at the forefront of software engineering for about a decade now, and most professional programmers have at least started to adopt object-oriented (OO) techniques. As more programmers gain experience in the OO methods, they are finding that the greatest power comes when writing programs based on the so-called "Hollywood principle"-don't call us, we'll call you.

Frameworks are collections of related classes, which often include their own thread of control. Out of the box, the framework constructs certain objects and calls certain methods. One of the first frameworks, for example, was MacApp. The unmodified MacApp framework put up a Macintosh menu bar and a simple window. The main event loop in that simple program watched for keypresses and mouse clicks. If the user clicked the File menu's Quit item, the application exited.

Today, application generators such as AppWizard in Microsoft's Visual C++ work much the same way. You use the wizard to describe the characteristics of the program you want to write, and the AppWizard builds a simple application by using the Microsoft Foundation Classes (MFC).

This concept is at the heart of Netscape's Internet Foundation Classes. All IFC-based applets are instances of the class NetscapeApplet. NetscapeApplet has its own thread of control, which removes an event from the event queue, determines its destination, and then delivers the event, executing the event processing code in the receiving object. Once this code completes, the IFC thread removes the next event and repeats the cycle.

In general, you can extend a framework in two ways to build a real application. First, you can use existing components of the framework, rearranging them to get the effect you want. Second, you can derive new classes based on framework classes, overriding methods in the base class to add new behavior. This second technique is often called subclassing. This chapter shows examples of both kinds of extension in the next section.

The first release of the IFC includes the following frameworks and related classes:

Tip
Most of the frameworks that are built into the first release of the IFC have to do with the user interface. The mechanisms for displaying and controlling your application's content often comprise 80% or more of your total code. By using the IFC's user interface classes, you can slash your total design and implementation time dramatically.

User Interface Classes

Some of the pioneers of OO methods, the engineers at Xerox's Palo Alto Research Center (PARC), developed an architecture based on the model, view, and controller (known as the MVC architecture). The model contains the actual content of a program-in many cases, the model is the same as a document. The user gets to look at the model in one or more ways, known as views and can send messages to the model by using the controller. This architecture has the advantage of each major functional component being separate from the others. A new kind of view does not force the model to change. A new control may be implemented that still uses existing methods in the model.

Many modern OO systems use some form of MVC architecture. Smalltalk programs use MVC almost exclusively. Microsoft Foundation Classes have the concepts of document and view. Taligent, the joint venture between IBM, Apple, and HP (now a subsidiary of IBM) includes models and views in their CommonPoint product (see http://www.taligent.com/). The IFC include a View class, which is used to implement both the view and the controller of the MVC architecture.

An IFC View is responsible for drawing things to the screen. Look at the documentation file Documentation/Reference/n.a.View.html that comes with the IFC. A portion of that file is shown here:

public class netscape_beta.application.View
    extends java.lang.Object
    implements netscape_beta.util.Codable
{
    /* Fields
     */
    ...
    public netscape_beta.application.Rect bounds;
    /* Constructors
     */
    public View();
    public View(Rect);
    public View(int, int, int, int);

    /* Methods
     */
    public DragDestination acceptsDrag(DragSession, int, int);
    ...
    public Rect bounds();
    public boolean canDraw();
    public void computeVisibleRect(Rect);
    public boolean containsPoint(int, int);
    public boolean containsPointInVisibleRect(int, int);
    ...
    public void disableDrawing();
    public boolean doesAutoResizeSubviews();
    public void draw(Graphics, Rect);
    public void draw(Rect);
    public void draw();
    public void drawSubviews(Graphics);
    public void drawView(Graphics);
    ...
    public int height();
    public int horizResizeInstruction();
    ...
    public void keyDown(KeyEvent);
    public void keyUp(KeyEvent);
    ...
    public boolean mouseDown(MouseEvent);
    public void mouseDragged(MouseEvent);
    public void mouseEntered(MouseEvent);
    public void mouseExited(MouseEvent);
    public void mouseMoved(MouseEvent);
    public void mouseUp(MouseEvent);
    ...
    public RootView rootView();
    public void scrollRectToVisible(Rect);
    public void setAutoResizeSubviews(boolean);
    public void setBounds(Rect);
    public void setBounds(int, int, int, int);
    ...
    public int width();
    ...
    public int x();
    public int y();
}

Note that View has lots of public methods that deal with drawing, such as setBounds() and draw(). The class also has methods that support controls. For example, you can derive a button class from View and override mouseUp() to cause the button to respond to a mouse click. (In fact, Netscape has already built such a class for you-it's called Button.)

The addSubView() method is used to build a hierarchy of views. For example, if you add a Button to the default background view (called the RootView), the Button is a subview of the RootView. When you tell a View to draw itself, it draws all the components in its subviews as well.

Using the UI Components  Listing 16.1 shows an example of a simple IFC-based applet that does all its work without subclassing.


Listing 16.1  -A Simple Applet that Uses Existing Components of the User Interface Framework

import netscape_beta.application.*;
import netscape_beta.util.*;

public class DrawDemo extends Application implements Target
{
  Button theButton;
   
  public void init()
  {
    super.init();
    mainRootView().setColor(Color.black);
    theButton = new Button(25, 150, 100, 25);
    theButton.setTarget(this);
    theButton.setTitle("Change color");
    mainRootView().addSubview(theButton);
    System.out.println("By default theButton sends command " +
      theButton.command());
  }
   
  public void performCommand(String command, Object arg)
  {
    System.out.println("Performing command " + command);
    if (mainRootView().color().equals(Color.black))
      mainRootView().setColor(Color.white);
    else
      mainRootView().setColor(Color.black);
    mainRootView().draw();
  }
}

Note
If you want to write a program like this one, without the IFC, you start by making an instance of AWT's button class and then by writing a handler for the mouse click in the applet's action() method. One advantage of using the IFC is that you get a much richer button class-you can set up radio buttons, toggles, and pushbuttons with fine control of the appearance, position, and behavior of each button. Another advantage is that your IFC Application has its own thread. While that fact doesn't make much of a difference here, in an applet that does work in the background, it's convenient to be able to return control quickly to the user.

This example says that it extends Application and implements Target. Application is IFC's implementation of the Java applet-don't confuse the IFC Application class with Java applications. Class Application has its own thread of control, so you can have more than one Application running on the same page at the same time.

The Target interface is a powerful mechanism for interobject communications. A class that implements Target announces that other objects can send commands to it, using its performCommand() method. Notice that there is no code in this example to handle mouse clicks. When the button receives a click, it sends its command to its target. The line

theButton.setTarget(this);

tells the button to aim its commands at the applet, which processes commands in its performCommand() method.

By inheriting from Application, your applet gets a default RootView, accessed by the function mainRootView(). Anything you want your applet to do when it is constructed should go in the applet's init() method. The first line in init() calls the init() method of the applet's parent class, Application. In this example, init() next sets the background color and installs theButton.

If you open the Java console window while this applet is running, you'll see the output of the two println() calls. After the button is installed, it announces its default command: command. When the button is clicked, it sends its command to its target (the applet), which runs performCommand() and reports that it saw the command command.

Tip
Use setTarget() to tell an object where to direct its commands. Use setCommand() to specify which command is sent to the target.
For an example, see the page Documentation/DeveloperGuide/targets.mak.html in the IFC documentation. On that page, Netscape shows a class MovieView, which includes the lines
static final String START = "start";
...
public void init(int x, int y, int width, int height){
  ...
  startButton = new Button();
  startButton.setTitle("Start");
  startButton.setTarget(this);
  startButton.setCommand(START);
  ...
}
To process this command, the class includes an implementation of the performCommand() method:
public void performCommand(String command, Object anObject) {
  if (START.equals(command)) {
    timer.start();
  } else if (STOP.equals(command)) {
      timer.stop();
    } else ...
      }
}

Tip
After changing a property of a View object (as this example changes the color of the RootView), be sure to call the View's draw() method to update the screen.

Subclassing the UI Components  The second way to extend a framework is to derive new classes from the framework's components-a technique called subclassing. Open the Aquarium example's file Aquarium.java and find the lines

// The tank should be exactly the size of the background
        tankView = new TankView(0, 0, mainRootView().width(), 
mainRootView().height());
        mainRootView().addSubview(tankView);

Instead of adding an existing framework class (Button), as the previous example did, Aquarium adds a derived class: TankView. Now open tankView.java to see the implementation of this class.

The class is declared to be a View with two additional interfaces (used to support animation and drag-and-drop):

public class TankView extends View implements
  DrawingSequenceOwner, DragDestination {
  ...
}

The TankView constructor loads the images of the sand and the water and allocates space for the image sequences that will be used for the animation. The programmer has overridden DrawView() so that it draws the sand and water and then iterates through all of the installed objects (that is, fish, plants, and bubbles), drawing their current image. The remaining methods are specific to DragDestination and DrawingSequenceOwner.

By using existing framework classes as a starting point, you avoid the work of writing low-level code such as View. The existing classes and interfaces also serve as a checklist, telling you what methods you should put in your class in order to get the desired behavior.

Caution
If you don't override the parent class's methods, the compiler will give you the default behavior. If the default behavior is acceptable, you avoid the work of writing the method.
You must provide implementations for the interface's methods-there is no default behavior, and the compiler will generate an error if you leave a method out.

Tip
The Aquarium example is a good source of information about drag-and-drop and animation. Use it as a starting point if your application requires these features.

Running an IFC Application as a Java Application  You can easily convert an IFC-based applet to a Java application by adding a main() method, which does the work of setting up the applet's environment. In general, you must do the following:

  1. Make a new instance of the applet class.
  2. Make a new external window.
  3. Set the size of the external window-the applet would otherwise get this size from the HEIGHT and WIDTH attributes of the HTML <APPLET> tag.
  4. Show the window.
  5. Map the window's default RootView to the application's RootView.
  6. Start the application's thread.

For example, here's the Aquarium example's main() method:

public static void main(String args[]) {
        Aquarium app;
        ExternalWindow mainWindow;
        Size size;

        app = new Aquarium();
        mainWindow = new ExternalWindow();
        size = mainWindow.windowSizeForContentSize(750, 650);
        mainWindow.sizeTo(size.width, size.height);
        mainWindow.show();
        app.setMainRootView(mainWindow.rootView());

        app.run();
    }

Application Classes

Recall that the IFC includes two packages: netscape_beta.application and netscape_beta.util. In practice, you will usually want to import both packages into your IFC-based classes. Classes in the application package expect an IFC Application to be present, with its event loop thread running. Classes in the util package may be used without an Application if desired.

If you want to see which classes are part of which package, check out /Documentation/Reference/Package-n.application.html and Documentation/Reference/Package-n.util.html in the IFC documentation.

Understanding Events  The distinctive feature of an IFC-based applet (or application) is the event-handling mechanism. Figure 16.5 illustrates this mechanism.

Figure 16.5: Sun's Abstract Windowing Toolkit (AWT) queues events for the IFC application thread.

When you click the mouse or press a key, Sun's Abstract Windowing Toolkit (AWT) detects the event and places it in a queue for the IFC-based application. Each instance of class Application has its own thread and an associated EventLoop. The EventLoop reads events out of the queue, looks up which EventProcessor should handle this event, and sends the event to the proper EventProcessor. Class EventProcessor has a method processEvent(), which handles the events passed to the processor.

For class MouseEvent, events are sent to a RootView, where RootView.processEvent() gets called. RootView.processEvent() looks at the coordinates of the mouse click and decides which subview (if any) should get the MouseEvent. Finally, the MouseEvent arrives at its destination View. If that View has a target (as, for example class Button does), that View sends a performCommand() message to the View's designated target, passing along the View's command and any associated data.

At last, the Event has been transformed into a message and the Target gets the performCommand() message and executes the associated command.

Threads and Timers  Sometimes you need to support a background task (such as updating an animation) while handling foreground events (such as mouse clicks and keypresses). One design is to run two threads and have each thread handle its own set of events. If you do this, you must be sure to write thread-safe code so that two threads do not compete over which one gets to update a variable. You need to make sure that methods in the threads that might access the same piece of data get the keyword synchronized in their declaration. You also need to make sure that each thread calls sleep() from time to time, so other threads get a chance to run.

Tip
If you program in a Windows environment you may have missed letting each thread sleep(). There's a defect in Netscape's Windows Java implementation-thread priorities are not handled correctly. Without calling sleep(), a thread may still be interrupted and another thread allowed to run.
Since other Netscape platforms (for example, UNIX and Macintosh) have a correct implementation of thread priority, you should get in the habit of calling sleep() somewhere in each thread's main loop. The paint thread is a low-priority thread-without letting your threads relinquish the processor from time to time, the paint thread will not get a chance to run on UNIX or Macintosh computers. If Netscape fixes this defect in a future Windows implementation, you could get the same misbehavior under Windows 95 if you don't add sleep().
For a more detailed discussion of this behavior, see the section titled "A Word About Thread Priority, Netscape, and Windows" in Chapter 25 of Special Edition Using Java (Que, 1996).

IFC do a perfectly good job of supporting thread-safe code, but you may not want to go through the work of managing multiple threads and ensuring synchronization to get just a simple multitasking effect. With IFC, you have the option of using timers to handle periodic events. For example, to periodically call a background task, like an animation sequence update, you might write

Target timer;
static final String NEXT_FRAME = "next frame";
public void init(...){
  ...
  timer = new Timer(this, NEXT_FRAME, 250);
  timer.start();
  ...
}

The line

timer = new Timer(this, NEXT_FRAME, 250);

says that the new timer should send the command NEXT_FRAME to the target this every 250 milliseconds. The target will get a performCommand() message with the command set to NEXT_FRAME.

Tip
The command mechanism used by timers is identical to the mechanism used by buttons and other controls. During testing, you may want to disable the timer and replace it with a button that sends the timer's command. That way, you can control when the "timer" ticks and trace the command through the application. Once you're satisfied that performCommand() is working correctly, you can restore the timer and run the function at full speed.

Caution
On a slower processor with a fast timer, the application may not be finished processing one command before the next command is queued. In some cases, more than one instance of the command may be queued. By default, timers coalesce their commands under these circumstances, so the application will get only one instance of the command.
If you don't want a timer to coalesce commands, call the Timer's setCoalesce(boolean flag) method and pass false in the flag.

Utilities

Not all classes in the IFC require an Application. The classes in netscape_beta.util may be used as a stand-alone or as part of an IFC Application. Most of these classes have to do with the process of archiving and restoring objects.

Persistent Objects  Classical databases, such as those that use the Structured Query Language, or SQL (pronounced see-quel), are concerned primarily with data and only secondarily with links between data. For example, a book wholesaler's database might have a Books table with data such as title, author, and publication date. The same database might have a table that lists the names and addresses of various publishers. One of the fields in the Books table could be the publisher's ID. If someone poses a query to the database like

SELECT books.title IN books, publishers
  WHERE publicationYear = 1996 AND
        publishers.city = "Indianapolis" AND
        books.publisherID = publishers.ID

then the database manager performs a join operation, searching through the publisher's table, finding IDs that match records in the Books table, and then selecting out records that meet the required publication year (in the Books table) and the required city (in the Publishers table). Fields that link two or more tables, as does books.publisherID here, are called foreign keys.

As the number of foreign keys in a design increases, the amount of work done by a relational database manager goes up dramatically. As a rule of thumb, when the number of foreign key fields gets to about 20% or more of the total number of fields, a designer should question whether their needs can be met adequately with a relational database manager.

Several companies, such as Object Design of Burlington, Massachusetts, offer sophisticated object-oriented databases. These database products offer SQL interfaces, so you can communicate with them by using native Java methods or JDBC, the Java Database Connectivity package. For simple applications, however, you may be able to just save the objects to the hard drive and restore them when you need them.

To archive a collection of objects, you declare one or more root objects-objects that contain pointers to all of the objects you want to archive. Each object to be stored must implement the Codable interface, which includes methods for encoding and decoding the object to and from a serializable format. Finally, you instantiate an Archive object and add the root object(s) to the Archive. You can save the Archive to the hard drive by using a DataOutputStream, by mapping it to a relational database, or by transmitting it over the Internet.

Tip
If you try to read or write to the hard drive from an applet, the browser's security manager will block you. If you need access to the local hard drive, run your code as a Java application.

To restore objects from the Archive, instantiate an Unarchiver and call its unarchiveRoots() method, passing the Archive as a parameter.

Note that an Archive is a serialized representation of the root object (and all the objects that object points to).

Tip
Because the Archive is written using its writeASCII() method, you can write it to a disk file and then write separate tools to manipulate the contents of the file. You can use this technique as a general-purpose import/export mechanism, transforming databases and files from other applications to something that is readable by your IFC-based application. Here's a sample of the archive written by archDemo.java:
{
    classTables = {
        ArchivableButton = { fieldNames = [ title ];
ÂfieldTypes = [ string ];};
    };
    classVersions = { ArchivableButton = 1;};
    instances = {
        ArchivableButton0 = { class = ArchivableButton; title =
Â"Change to Blue";};
    };
    rootInstances = [ ArchivableButton0 ];
}

Building a Simple Database with Hashtable  Both the Java JDK and the IFC include a Hashtable class, which can add powerful database-like features to your programs. A hash table is a simple data structure that allows fast access to data elements by keys. Figure 16.6 illustrates a hash table.

Figure 16.6: A hash table can allow 0(1) lookup, at the expense of some memory.

Each item in a hash table consists of a key and an element. When an item is added to a hash table, the key is passed through a hash function, which is designed to spread similar keys over a large numeric range. The new entry is attached to the hash table under that entry's hash code. In Figure 16.6, for example, the key Republican has a hash code of 126805.

To look up an entry, the Hashtable class computes the hash code of the key and looks at that entry in the table.

Note
A Java hash table is specified by its capacity and a load factor-a unitless figure between 0.0 and 1.0 which specifies how full the hash table is allowed to become before it is expanded. If the hash table becomes relatively full, some new entries will inevitably have keys that map to hash codes already in use. When this happens, the hash table strings all of the entries for a given hash code together, and the Hashtable class must search down that linked list.
Occasionally having to search a list of two or three entries doesn't change the performance of the hash table much. Keeping the table capacity small and the load factor high can save memory in the application. On the other hand, memory has never been less expensive, so its not a bad idea to preset the hash table large enough that duplicate hash codes are rare.
You can control the capacity and load factor of a java.util.Hashtable through one of that class's constructors. The IFC version of Hashtable doesn't allow you to specify the load factor, but you can specify the capacity in the constructor.

Computer scientists measure algorithm and data structure speed by using big-O notation. A system that looks up an entry in a data structure in an amount of time that is proportional to the number of entries in the data structure is said to demonstrate O(n) performance. The hash table demonstrates O(1) performance-most lookups can be returned in a single step.

Recall that a class cannot be saved to an archive unless it implements the Codable interface. Furthermore, the entries in the Hashtable must also be Codable. The IFC version of Hashtable satisfies the first requirement. ArchivableString implements Codable, so instances of ArchivableString can be saved in an Archive.

The hashWrite class makes a new IFC Hashtable and saves a series of key-element pairs. Every key and every element is an ArchivableString. Once the Hashtable has data, hashWrite writes it out to a local file named archive. (Run the hashWrite.class application as a stand-alone application by using the Java interpreter-the archive file will be written to the same directory as the Java interpreter.)

Once you have an archive file, run hashRead as a standalone application and pass the file name archive as a command-line parameter. The application reads the archive from the hard drive and unarchives it into an IFC Hashtable. Each of the three buttons (Democrat, Reform, and Republican) corresponds to one of the keys in the Hashtable.

When you click a button, the application outputs a series of demonstrations about the entries in the Hashtable. With version 1.0.2 of the JDK, java.util.Hashtable does not return the entry, even though the hash codes match. This defect should be fixed in version 1.1. As a work-around, hashRead copies the codeable entries in the IFC Hashtable into String entries in a java.util.Hashtable. Working directly with the Strings, Hashtable.get() works correctly and displays the proper data in the TextField.

The Future of IFC

Netscape has announced a series of new frameworks that will be part of the next release of the IFC, including new user interface components and an enhanced security manager. Keep an eye on http://developer.netscape.com/library/ifc/ and the DevEdge private pages to find out which new frameworks are available.

Java and the IFC are important components of Netscape ONE and the Sun JDK is an integral part of these components. Unfortunately, the Sun JDK is essentially a command-line driven compiler and class library with a few utilities thrown in. Users familiar with Microsoft Visual C++ or a similar professional development environment will miss the integration of editor, compiler, and documentation.

Some vendors (for example, Borland and Metrowerks) are integrating Java into their C++ development environments. Other vendors are bringing out whole new Java-centered development systems (for example, Natural Intelligence's Roaster). Netscape has announced that they are working on an integrated environment of their own, to be called Netscape Constructor. Constructor will be centered on the IFC, with a strong component of "visual building." Third-parties and developers will be able to add their own components to the IFC classes that are included with Constructor. Beta releases of Constructor are likely to be made available first to members of DevEdge, Netscape's developer support program. If you've been waiting for an excuse to join, now you have one.

Connecting to IFC-Based Applets Through LiveConnect

LiveConnect is Netscape's integration technology that allows Navigator plug-ins and JavaScript to communicate with Java applets. Part V, "LiveConnect," describes this technology in more detail.

When you call an IFC-based applet from JavaScript, you need to surround the calls to the applet's functions with calls to pushIFCContext() and popIFCContext(). The first call, pushIFCContext(), returns the Java application. Here's a skeleton for calling IFC-based applets from JavaScript. Fill in the italicized words with the name of your function, application object, and application method(s). You only call pushIFCContext() and popIFCContext() from JavaScript, never from inside a Java program.

<SCRIPT>
  function aJavaScriptFunction()
  {
    theApp = document.applets[0].pushIFCContext();
    theApp.anAppObject.anAppMethod();
    document.applets[0].popIFCContext();
  }
</SCRIPT>

Caution
Be sure to call popIFCContext() at the end of the JavaScript function. If you don't, the Java runtime environment may eventually run out of memory, and the user's browser will crash.

In the SDK…

This chapter describes the Netscape Internet Foundation Classes. You can use the IFC to decrease the time and labor required to build Netscape ONE applications. To use the IFC, you need to write in Java. In many applications, you may want to integrate the IFC-based Java applet with JavaScript and with Navigator plug-ins.

ON THE WEB
http://developer.netscape.com/library/ifc  Download the latest version of the IFC from Netscape's developer site. If you're a member of Netscape's developer program, DevEdge, check the members-only site to see if Netscape has an even newer version available.