Chapter 12

Putting Them All Together


CONTENTS


framework \fram-wurk\ n: a basic structure

Introduction

The previous four chapters have introduced you to some new and exciting Java classes, all of which can be used in the development of intranet applications. However, as individual classes, they have no context, no greater purpose in life. You're probably wondering how you can use all of their features in your own applications as well. This chapter gives you the direction you need to use the classes.

First, the concept of Java packages is discussed. These allow you to bundle classes together in a manner that is easily maintained and (re)used. The classes then have a purpose. They are no longer individual classes; together they become a class library.

You then explore a few new classes that draw upon the power of the new package. These new classes, in conjunction with the foundation classes discussed in the previous chapters, become the Java Intranet Framework, or JIF. JIF applications implement the model intranet application as discussed in Chapter 7, "A Model Intranet Application." As you see, this framework does much of the drudgery for you and allows you to concentrate your efforts on the content of the application.

Java Compilation Basics

Programming languages typically exist at a higher level. This means that after you create source code, it is translated or compiled into a language that the computer can understand. After all of your source code has been translated, the final result is assembled or linked into a single entity. This entity is your program. It has been translated from the higher-level language into a language your computer can read and execute for you. Figure 12.1 illustrates this concept.

Figure 12.1 : The creation of a program.

The files created in this intermediate step are typically called object modules. These modules are pockets of CPU instructions that are created from your source code. These modules are then concatenated and massaged to produce your program.

Java Source Code Files

Creating Java programs entails writing a set of classes. You create Java classes in source code format. These files have the .java extension on them. Typically, each .java file contains the source code for a single class. This class name is also the name of the file. For example, if you have a class called LightBulb, it is stored in a file called LightBulb.java.

Tip
A .java file might contain the source code for more than one class. However, only one of the classes in the source code file can be declared public.
This allows you to bundle a private class with another class that uses it. A good example of this bundling is for manager classes. You can create a class that manages a collection of objects. The objects can be represented by an entire class. Because it is a private class, you can store it in the same source code file with your manager class.
Look in the java.sql.DriverManager class on the CD-ROM for an example of this.

Some languages such as Visual Basic or PowerBuilder actually compile your program into tokens or pseudocode. These instructions are used to tell an interpreter what to do. The final result of these languages is actually an interpreter bundled with the tokenized code.

Java is different from typical languages but similar to languages such as Visual Basic. Your Java source code is compiled into a pseudocode format called byte code format. But instead of creating object modules, the Java compiler creates class files that hold this byte code format. These files end with a .class extension. Using the LightBulb class as an example, it is compiled into a file called LightBulb.class.

Each of these class files contains the byte code for a single Java class. These class files can then be executed by the Java Virtual Machine or JVM. It might seem like a fancy name for an interpreter but it truly is a machine. Think of the JVM as a software engine. Your Java classes are the gasoline that makes it run. But unlike typical programming languages, the final linkage is not performed for Java programs.

What? No Linker?

That's right, Java performs runtime linking. This is a function of the JVM. As your Java program runs, the JVM loads each class as it needs it. When your class no longer exists in your program, the JVM discards the memory copy. But how can it find all those classes? They can be stored almost anywhere.

The JVM searches for these classes on your hard disk using a predefined set of directories. These directories are specified using an environment variable called CLASSPATH. It holds the list of places to look for your classes.

CLASSPATH

In order for the JVM to find your classes, you must tell it where to start looking. This is done with the CLASSPATH environment variable. This variable contains a list of the directories on your hard drive that contain Java .class files. The list is separated by a colon (:) or a semicolon (;) depending on your computer platform.

Note
UNIX systems use the colon (:), and Microsoft Windows-based systems use the semicolon (;).

Here is an example of a Microsoft Windows platform CLASSPATH:

.;c:\jdk\lib\classes.zip;c:\JavaWork\Classes;

This tells the JVM to look in the current directory first (.), then search in the ZIP file c:\jdk\lib\classes.zip, and then look in the c:\JavaWork\Classes directory.

A similar CLASSPATH for UNIX looks like this:

.:/jdk/lib/classes.zip:/JavaWork/Classes

The JVM searches in the current directory and then searches in the /jdk/lib/classes.zip file, and finally searches in the /JavaWork/Classes directory.

Tip
It is possible to specify the CLASSPATH without using an environment variable. To do this, you must use the -classpath argument when running the Java interpreter java. For example, the command
java -classpath
.;c:\jdk\lib\classes.zip;c:\JavaWork\Classes LightBulb
sets the CLASSPATH and runs the program LightBulb

If you stick with this model and make 500 classes, you have to store them in the c:\JavaWork\Classes directory. This becomes cluttered and very disorganized. Isn't there a better way to organize the classes? Glad you asked, because there is.

You can organize your code into packages.

Have You Got the Package?

In typical programming environments, object modules can be stored in what are known as code libraries. These libraries can consist of any object module you really care to place into them. Libraries are excellent places to store reusable code, because many linkers can read code out of code libraries in addition to reading the object modules themselves.

But because Java doesn't really have any object module per se, it must use a different method for archiving classes. Enter the Java package.

A package is a collection of Java classes. These classes usually have nothing in common but their purpose because Java packages are used to bundle functionality. You really shouldn't place utility classes in a graphics package.

Java itself comes bundled in six major packages. These are shown in Table 12.1.

Table 12.1. The Java packages.

PackageDescription
java.applet Applet classes
java.awt Advanced Windowing Toolkit classes
java.io Input and Output classes
java.lang The Java language classes
java.net Networking classes
java.util Utility classes

The name of the package is very important. As you can see in Table 12.1, all of the Java packages start with java followed by a period and then the rest of the package name. Not only does this clarify the name of the package, but it defines the placement of these classes within the CLASSPATH.

A Sample Package

For example, suppose you create a set of packages for producing music. The set consists of three packages: a utilities package, a user interface package, and a sound package. These are named music.util, music.ui, and music.sound, respectively.

When the JVM looks for these classes, it replaces the period with a slash and appends the result to each directory in the CLASSPATH variable. It then tries to load this file. The name of the package additionally defines where it is stored in the CLASSPATH.

In this example, the music classes are under the c:\JavaWork\Classes directory. Then they are stored as follows:

music.util is stored in c:\JavaWork\Classes\music\util
music.ui
is stored in c:\JavaWork\Classes\music\ui
music.sound
is stored in c:\JavaWork\Classes\music\sound

The classes that make up each package are then stored in their respective directories.

Making Java Packages

Creating packages out of your source code is quite simple. The first step is to create a directory structure that is like the package name you use. Using the preceding example, you create the c:\JavaWork\Classes\music directory and then the three directories-util, ui, and sound-underneath it.

All of the source code that belongs in each package is then moved to its appropriate directory. All of the utility classes move to util, user interface classes move to ui, and sound classes move to sound.

The final step is to add a package statement at the top of your source code file that tells the compiler which package your class belongs to. This statement must be the first non-comment or blank line in your source code.

The format for a package statement is as follows:

package package_name

package_name is the fully qualified name of the package. For example, classes in the music.ui class have the following package statement:

package music_ui

Placing your class into a package has a very important implication. Your class now has access to all public and non-public classes in its package. Your class also has access to all non-private methods and instance variables of each other class in the package.

Caution
When you define an instance variable and do not assign it an access modifier (for example, private, protected, public), it defaults to package. This is essentially private but all classes within the same package can see it.

A similar arrangement exists in the C++ programming language. You can optionally declare a class to be a friend of another class. This allows it access to any non-private methods or instance variables in the target class. Think of packages as automatic friends.

Introducing the Java Intranet Framework

Chapter 7, "A Model Intranet Application," defines the functionality that a typical intranet application has. To recap, here are the features:

In Chapters 8 through 10, you designed and created many classes that implement these features. For instance, the ConfigProperties class implements the configuration file processing. And the DiskLog class implements your standard logging.

If you take a step back and look at what you've created, it's really a framework for developing intranet applications with Java. It truly is a Java intranet framework! Therefore, you name this collection of classes the Java Intranet Framework. And like any good programming toolkit or library, it has a unique and pronounceable acronym: JIF.

But now that you've created all of these classes, you need to organize them into a usable set of packages. This helps you to document, use, and later extend, JIF to its fullest potential. So how do you package the classes in JIF?

Packaging the JIF Classes

The best way to package JIF is in groups of functionality. As you recall, you have four functionality groups: utilities, logging, database, and user interface. These fit perfectly into four functionality packages.

Naming your packages is quite simple. You use jif as your base package name and then add on the specifics. For example, the utilities package is called util, as in java.util. Table 12.2 shows the package names you have chosen and the class functionality that is contained within them.

Table 12.2. The Java Intranet Framework packages.

PackageDescription
jif.awt Standard user interface classes and java.awt extensions
jif.log Standard logging classes
jif.sql Standard database connectivity classes and java.sql extensions
jif.util Standard utility classes and java.util extensions

The jif.sql package name does not really contain SQL functionality. However, the name is chosen to be consistent with the JDBC class hierarchy that lives in the java.sql package.

To package your classes, you must go back and edit each Java source code file. In each file you add a line of code that defines the package to which the class belongs. These statements are package statements, as discussed earlier in this chapter.

In addition to adding the package statements to our source code, you must also move all of the classes into a directory hierarchy that represents your package hierarchy. This hierarchy is illustrated in Figure 12.2.

Figure 12.2 : The JIF class/directory hierarchy.

Remember that the base of this hierarchy can live anywhere. It can be a root directory or a subdirectory. However, the directory that contains it must be in your CLASSPATH variable in order for the JVM to find the JIF classes.

Extending the Framework

Now that you've sorted your classes into usable packages, you can start building intranet applications. However, the classes really give you no direction or structure.

An excellent implementation of application direction and structure is Java's own Applet class. This class is a self-contained mini-application that runs in a special applet-viewer program or from an HTML World Wide Web page.

Java Applets

Creating applets is an easy task. Granted, the more complex your program is, the more
complex your applet is. However, applets can be quite simple because they have an entire
framework beneath them to support their simplicity. The smallest applet possible is shown in Listing 12.1. The HTML file that launches the small applet is shown in Listing 12.2, and the output of the applet is shown in Figure 12.3.

Figure 12.3 : The smallest applet.


Listing 12.1. The smallest applet.
//****************************************************************************
//* Imports                                         &nb sp;                        *
//****************************************************************************

//    Java Imports...
import                            java.applet.Applet;
import                            java.awt.Label;

//****************************************************************************
//* SmallApplet                                          ;                     *
//****************************************************************************

public class
SmallApplet
extends Applet
{

//****************************************************************************
//* init                                                                      *
//****************************************************************************

    public void
    init()
    {
        add( new Label( "I'm a small applet" ) );
    }

//****************************************************************************
//* run                                          & nbsp;                           *
//****************************************************************************

    public void
    run()
    {
        show();
    }
}

As you can see, the init() method is used to implement the user interface for your applet. After the init() method is called by the applet framework, the applet framework then calls the run() method. In the preceding example, the run() method does nothing more than show the applet on the screen.


Listing 12.2. The smallest applet's HTML page.
<HTML>
<HEAD>
<TITLE> A small applet </TITLE>
</HEAD>
<BODY>

<APPLET CODE="SmallApplet.class" WIDTH=200 HEIGHT=60></APPLET>

</BODY>

</HTML>

The applet framework provides several other useful facilities. One feature is the getParameter() method. This retrieves a parameter from the HTML file that launches the applet.

For example, consider the HTML file shown in Listing 12.2. It contains two parameters, WIDTH and HEIGHT. Through the use of the getParameter() method, you can retrieve the value specified after the equal sign (=), like this:

int myWidth = Integer.parseInt( getParameter( "WIDTH" ) );

This holds true for any property pair that appears in the HTML file. This is a convenient way to pass parameters to applets with an equally easy method of retrieving them.

What if you make JIF as easy to use as an applet? What if you create intranet applications that implement the model application features as easily as creating applets? Let's explore that possibility.

Making JIF Easy to Use

The first and most important thing you need to do is choose a name for your new bundle of joy. Let's call it a jiflet. Because you basically take many of the applet ideas from Java and place them into your own new class, it seems rather appropriate. Now that you have that out of the way, you define what your jiflet does.

The jiflet is a Java application that provides the features of your model intranet application in an easy-to-use wrapper. These features include your model features outlined earlier, plus many of the features in a standard Java applet.

One such feature is an init() method that is called automatically by the framework. This is a centralized location to place all of the initialization code.

Another feature is getting configuration parameters. You are able to retrieve parameters from the configuration file with a method such as the java.Applet.getParameter() method.

In fact, the following methods are available in both applets and jiflets:

The only features really missing from jiflets that appear in applets are the multimedia
methods. These methods provide a clean way for applets to load images and sound files off of a network. Because jiflets represent intranet applications, these features are not usually necessary.

With the design goals laid out, let's look into implementation of the classes.

The JifApplication Interface

To implement the jiflet, you employ the use of an interface. This interface defines the pattern or template to which all jiflets must adhere. There is a single method defined in JifApplication. In fact, Listing 12.3 shows the source code for JifApplication.


Listing 12.3. The JifApplication interface.
//****************************************************************************
//* JifApplication                                        &n bsp;                  *
//****************************************************************************

public interface
JifApplication
{

//****************************************************************************
//* init                                                                      *
//****************************************************************************

    public void
    init();

}

This interface requires all jiflets to have an init() method. The rest of the methods that you've designed into the class exist in the Jiflet class. However, the init() method must be created by the user of the class. Using this interface allows you to declare your Jiflet class abstract. This means that you cannot instantiate the Jiflet class alone. You must always derive a class from it.

The Jiflet Class

The Jiflet class is the intranet application version of Java's own Applet. It provides you with a framework to develop intranet applications quickly and easily. It brings together all of the usefulness of the other JIF packages into a single, easy-to-use, component.

The Jiflet class implements the JifApplication interface as well as the Log interface (described in Chapter 9, "Logging Classes"). This allows it to have an init() method and adhere to the standard log interface.

Let's take a look at the Jiflet class in detail. You start with the instance variables. You then move to the constructors and then on to each of the methods. After finishing this chapter, you should have a good understanding of the Jiflet class and how to use it.

Instance Variables

The Jiflet class contains the following instance variables:

protected boolean               activeFlag = false;
protected DiskLog               appLogFile;
protected String                appName;
protected boolean               appVerbosity = false;
protected ConfigProperties      configProperties;
protected ScreenLog             defaultLog;
private DBConnector             myConnector = null;
protected StatusBar             myStatusBar = null;
private int                     oldCursor = -1;

The following sections look at each one and how it is used.

activeFlag

protected boolean               activeFlag = false;

The activeFlag is used to denote the activity of a jiflet. Its state can be queried by way of the Jiflet.isActive() method. It is set to true right before the run() method is called and set to false after the destroy() method is called.

appLogFile

protected DiskLog               appLogFile;

The appLogFile is the log file object for the jiflet. During construction, this object is created like so:

appLogFile = new DiskLog( logPath, DiskLog.createLogFileName(), appName );

logPath is a configurable location in which to place all log files. The DiskLog.createLogFileName() method creates a standard log file name. Finally, appName is used on the standard log entry to identify the application that generates it.

appName

protected String                appName;

This string holds the name of the application that is running. This is usually passed in at construction.

appVerbosity

protected boolean               appVerbosity = false;

If you choose, your jiflet can be configured to report more information about certain things. This verbosity level is turned on or off with this Boolean instance variable. It defaults to false and can be set with the Jiflet.setVerboseMode() method.

configProperties

protected ConfigProperties      configProperties;

This object holds the combined program arguments and the configuration parameters
read from the application's configuration file. Access to this variable is through the Jiflet.getParameter() methods.

defaultLog

protected ScreenLog             defaultLog;

This variable is a default log in case the real application log cannot be created. This log writes its entries to the standard out of the operating system.

myConnector

private DBConnector             myConnector = null;

This variable holds the DBConnector object that is associated with this jiflet. You can set and get this variable with the Jiflet.setConnector() and Jiflet.getConnector() methods.

myStatusBar

protected StatusBar             myStatusBar = null;

This variable holds the instance of the StatusBar object that is created for the jiflet. The status can be set or cleared with the Jiflet.showStatus() and Jiflet.clearStatus() methods.

oldCursor

private int                     oldCursor = -1;

This private variable is used to store the value of the cursor while a "wait" cursor is displayed. This variable is used by the Jiflet.startWait() and Jiflet.endWait() methods.

Constructors

There are four ways to construct a jiflet. These are defined by four separate constructors. A constructor is the function that is called when an instance of your object is being created. Each of them are useful for different purposes. For the most part, most of your jiflets use the fourth incarnation. Let's take a look at each constructor.

Three of the constructors call the fourth constructor. This master constructor is where all the jiflet initialization takes place. The following is the complete source code for this constructor:

/**
 * Creates a Jiflet with a title, a name, arguments, and optionally
 * verbose.
 *
 * @param title The window title
 * @param name The name of the application
 * @param args The arguments passed in to the program
 * @param verbosity On/Off setting indicating verbosity of log entries
 * @see #setVerboseMode
 */
public
Jiflet( String title, String name, String args[], boolean verbosity )
{
    //    Call the superclass...
    super( title );

    //    Copy title to name...
    if ( name.equals( "" ) )
        name = title;

    //    Set the color...
    setBackground( Color.lightGray );

    //    Center and show our window!
    center();

    //    Add a status bar...
    enableStatusBar();

    //    Save my application name...
    appName = name;

    //    Create a default log....
    defaultLog = new ScreenLog( appName );

    //    Parse any passed in arguments...
    //    Parse the configuration file if available...
    configProperties = new ConfigProperties( args, appName + ".cfg" );

    //    Reset the title...
    setTitle( getParameter( "Title", title ) );

    //    Construct a log file name...
    String logPath = getParameter( "LogPath", "" );

    //    Open the log file...
    try
    {
        if ( logPath.equals( "" ) )
        {
            appLogFile = new DiskLog( appName );
        }
        else
        {
            appLogFile = new DiskLog( logPath,
                DiskLog.createLogFileName(),
                appName );
        }
    }
    catch ( IOException e )
    {
        //    Write errors to the screen...
        errorLog( "Error opening log file for [" +
            appName + "] (" + e.toString() + ")" );

        appLogFile = null;
    }

    //    Turn on verbose mode...
    setVerboseMode( verbosity );

    //    Denote construction...
    log( "Application [" + appName + "] started at " +
        ( new Date() ).toString() );

    //    Call my init!
    init();

    //    We are now active!
    activeFlag = true;

    //    Call my run...
    run();
}

Because the Jiflet class descends from Java's Frame class, you need to call the superclass's constructor with a title, which is what you do first.

The following are done next:

The following are the other three constructors available for creating Jiflet objects:

public
Jiflet()
{
    this( "Generic Jiflet", "Jiflet", null, false );
}

public
Jiflet( String title )
{
    this( title, "", null, false );
}

public
Jiflet( String title, String name, String args[] )
{
    this( title, name, args, false );
}

As you see, they all call the main constructor, setting some values to null or blanks.

Methods

There are many methods available in the Jiflet class. The following sections document their arguments and the purposes they serve.

setVerboseMode

public void
setVerboseMode( boolean whichWay )

This method turns on or off verbose mode. If verbose mode is turned on, all log entries created with the Jiflet.verboseLog() method are actually passed to the log object. If verbose mode is turned off, all log entries created this way are ignored.

Tip
This, in conjunction with Jiflet.verboseLog(), offers an excellent debugging tool. Simply enable verbose mode when you need more detail, and in your code provide that detail with the Jiflet.verboseLog() method.

verboseLog

//****************************************************************************
//* verboseLog                                                                *
//****************************************************************************

    public void
    verboseLog( char logLevel, String logEntry )

    public void
    verboseLog( String logEntry )

This method creates a log entry that is written to the log file only if the verbose mode flag is set to true.

The second constructor defaults to a log level of I for informational.

errorLog

//****************************************************************************
//* errorLog                                         &n bsp;                       *
//****************************************************************************

    public void
    errorLog( String logEntry )

This method allows you to create error log entries without specifying the Log.ERROR logging level each time. It is simply a convenience method.

log

//****************************************************************************
//* log                                          & nbsp;                           *
//****************************************************************************

    public void
    log( char logLevel, String logEntry )

    public void
    log( String logEntry )

This method creates a log entry that is written to the log file. If there is an error or the log file is not open, the output goes to the screen.

The second constructor defaults to a log level of I for informational.

handleEvent

//****************************************************************************
//* handleEvent                                          ;                     *
//****************************************************************************

    public boolean
    handleEvent( Event anEvent )

This overrides the default Frame.handleEvent() method. It listens for destruction events so that the jiflet can close itself down cleanly.

action

//****************************************************************************
//* action                                         &nbs p;                         *
//****************************************************************************

    public boolean
    action( Event event, Object arg )

This method receives ACTION_EVENT events from the event system. It listens for menu events and passes them to Jiflet.handleMenuEvent().

handleMenuEvent

//****************************************************************************
//* handleMenuEvent                                        & nbsp;                 *
//****************************************************************************

    protected boolean
    handleMenuEvent( Event event, Object arg )

This is a placeholder for menu events. This does nothing in the Jiflet class. It must be overridden by derived classes to include any functionality.

shutDown

//****************************************************************************
//* shutDown                                         &n bsp;                       *
//****************************************************************************

    public boolean
    shutDown( int level )

This method is the central point of exit for the jiflet. With the exception of a program crash, the jiflet always exits through this method. It is responsible for writing a log entry for the application ending, and it calls the Jiflet.destroy() method. The level argument is passed to the operating system as a return value for the calling program.

suicide

//****************************************************************************
//* suicide                                         &nb sp;                        *
//****************************************************************************

    public void
    suicide( Exception e, String logLine, int level )

    public void
    suicide( String logLine )

    public void
    suicide( String logLine, int level )

    public void
    suicide( Exception e )

    public void
    suicide( Exception e, String logLine )

This method allows a jiflet to gracefully kill itself. Depending on the circumstances, you may or might not want a log entry written, and you may or may not have an Exception that caused your death.

center

//****************************************************************************
//* center                                         &nbs p;                         *
//****************************************************************************

    public void
    center()

This method centers the jiflet window on the screen.

enableStatusBar

//****************************************************************************
//* enableStatusBar                                        & nbsp;                 *
//****************************************************************************

    public void
    enableStatusBar( String text )

    public void
    enableStatusBar()

This method creates a status bar with or without text and adds it to the jiflet's layout.

clearStatus

//****************************************************************************
//* clearStatus                                          ;                     *
//****************************************************************************

    public void
    clearStatus()

This method clears the text in the status bar.

showStatus

//****************************************************************************
//* showStatus                                                                *
//****************************************************************************

    public void
    showStatus( String text )

This method sets the text in the status bar.

setConnector

//***************************************************************************
//* setConnector                                        &nbs p;                    *
//****************************************************************************
*
    protected void
    setConnector( DBConnector aConnector )

This method sets the DBConnector object that is associated with this jiflet.

getConnector

//****************************************************************************
//* getConnector                                        &nbs p;                    *
//****************************************************************************

    public DBConnector
    getConnector()

This method returns the previously associated DBConnector object to the caller.

startWait

//****************************************************************************
//* startWait                                         & nbsp;                      *
//****************************************************************************

    public void
    startWait()

This is a luxury method. It changes the cursor to the system default "wait" cursor, which is usually an hourglass to indicate that a lengthy process is occurring.

endWait

//****************************************************************************
//* endWait                                         &nb sp;                        *
//****************************************************************************

    public void
    endWait()

This method returns the cursor to its previous state if called after a Jiflet.startWait() call. Otherwise, this method does nothing.

getParameter

//****************************************************************************
//* getParameter                                        &nbs p;                    *
//****************************************************************************

    public String
    getParameter( String key )

    public String
    getParameter( String key, String defaultValue )

This method is identical to Java's Applet.getParameter() method. It returns the value associated with the key passed. You can also pass in a default value in case the key is not found. These parameters are looked for in the configProperties instance variable.

canClose

//****************************************************************************
//* canClose                                         &n bsp;                       *
//****************************************************************************

    public boolean
    canClose()

This method is called before the jiflet is allowed to close. It allows you to have a place to catch an unwanted departure. The default implementation returns true. Override this method to add your own functionality. An example of its use is to catch users before exiting if they haven't saved their work.

Within your overridden copy, you can ask users whether they want to save their work. If they say no, return true, closing the jiflet. If they say yes, save their work and then return true, closing the jiflet. You can also offer them a cancel option, which returns false.

isActive

//****************************************************************************
//* isActive                                         &n bsp;                       *
//****************************************************************************

    public boolean
    isActive()

This method returns true or false if the jiflet is currently active. This is similar to the Applet.isActive() method. A jiflet is considered active right before its run() method is called.

Wrapping Up Jiflets

Because you have these two new classes, you need to place them somewhere. Again you borrow from the example set by Sun and place them in a package called jif.jiflet. This package contains the JifApplication and Jiflet classes.

Programming with Jiflets

Now that you've created a class that implements the design goals, let's take it for a test drive. Remember your simple small applet earlier in this chapter? Let's see if you can make a jiflet just as easily.

The Smallest Jiflet

The smallest possible jiflet is similar to the smallest possible applet. It simply prints a string on the screen and does nothing else. Let's look at the source code for your smallest jiflet:

//****************************************************************************
//* Imports                                         &nb sp;                        *
//****************************************************************************

import                            jif.jiflet.Jiflet;
import                            java.awt.Label;

//****************************************************************************
//* SmallJiflet                                          ;                     *
//****************************************************************************

public class
SmallJiflet
extends Jiflet
{

//****************************************************************************
//* main                                                                      *
//****************************************************************************

    public static void
    main( String[] args )
    {
        new SmallJiflet();
    }

//****************************************************************************
//* init                                                                      *
//****************************************************************************

    public void
    init()
    {
        add( "Center", new Label( "I'm a small jiflet" ) );
        pack();
    }

//****************************************************************************
//* run                                          & nbsp;                           *
//****************************************************************************

    public void
    run()
    {
        show();
    }

}

It is very similar to the small applet in Listing 12.1. The only additions are a main() method and a call to the jiflet's pack() method.

The main() method is called by the Java interpreter to run your program. It is required to create the class that is your program. Here you create an instance of SmallJiflet.

The pack() method readjusts the size of the jiflet to accommodate all the user interface objects that are contained within it. This is necessary because, unlike an applet, you have no predefined width or height.

The output of your small jiflet is shown in Figure 12.4.

Figure 12.4 : The output of SmallJiflet.

The HelloWorld Jiflet

Do you think that you aren't going to get a Hello World jiflet? Think again. It is one of the simplest of all jiflets. All programming languages start with a program that prints Hello World!, so why is Java any different?

The source code in Listing 12.4 is the complete source code for the HelloWorld application that is on the CD-ROM. This is the smallest jiflet possible.

This jiflet is a bit more complex than the previous smallest jiflet. In HelloWorld you add a menu and an About box. You also take advantage of the handleMenuEvent() functionality in your base class.


Listing 12.4. The HelloWorld application.
//****************************************************************************
//* Imports                                         &nb sp;                        *
//****************************************************************************

//    JIF Imports
import                             jif.jiflet.*;
import                            jif.awt.*;

//    Java Imports...
import                            java.awt.*;

//****************************************************************************
//* HelloWorld                                                                *
//****************************************************************************

public class
HelloWorld
extends Jiflet
{

//****************************************************************************
//* Members                                         &nb sp;                        *
//****************************************************************************

    //    A message for the about box...
    String                        copyright = "HelloWorld v1.00\n" +
        "Copyright (c) 1996 by Jerry Ablan\n" +
        "All Rights Reserved";

    //    Menu stuff...
    Menu                         helpMenu;

//****************************************************************************
//* main                                                                      *
//****************************************************************************

    public static void
    main( String args[] )
    {
        new HelloWorld( args );
    }

//****************************************************************************
//* Constructor                                          ;                     *
//****************************************************************************

    public
    HelloWorld( String args[] )
    {
        super( "Hello World!", "HelloWorld", args );
    }

//****************************************************************************
//* init                                                                      *
//****************************************************************************

    public void
    init()
    {
        //    Initialize my menus...
       initializeMenus();

        //    Initialize my UI...
        initializeUI();
    }

//****************************************************************************
//* initializeMenus                                        & nbsp;                 *
//****************************************************************************

    public void
    initializeMenus()
    {
       MenuBar mb = new MenuBar();

       helpMenu = new Menu( "&Help" );
       helpMenu.add( new MenuItem( "&About..." ) );
       mb.add( helpMenu );

        mb.setHelpMenu( helpMenu );

        setMenuBar( mb );
    }

//****************************************************************************
//* initializeUI                                        &nbs p;                    *
//****************************************************************************

    public void
    initializeUI()
    {
        JifPanel ap = new JifPanel( Effects.RAISED, "Hello World!" );
        ap.setFont( new Font( "Helvetica", Font.BOLD, 18 ) );
        add( "Center", ap );
        pack();
    }

//****************************************************************************
//* handleMenuEvent                                        & nbsp;                 *
//****************************************************************************

    protected boolean
    handleMenuEvent( Event event, Object arg )
    {
        String label = ( String ) arg;

        if ( label.equalsIgnoreCase( "&About..." ) )
        {
            MessageBox id = new MessageBox( this, "About...", copyright );
            id.center( true );
            id.show();
            return( true );
        }

        //    Didn't handle it!
        return( false );
    }

}

Figure 12.5 shows the output of the HelloWorld program.

Figure 12.5 : Someone says hi!

The program is simple. First of all, it extends the Jiflet class. This gives you access to the functionality of the base class. Second, it provides an init() method. This method is called when the jiflet is initialized and ready for the user portion to be initialized. After that, all that is left is to respond to events that come up.

Extending Jiflets for Real-World Use

Now that you have a basic understanding of the Java Intranet Framework or JIF, the rest of this chapter extends the jiflet concept into a form that can easily be used for building database-aware intranet applications.

A jiflet, as you know, is the smallest application that can be built with JIF. However, it does absolutely nothing but look pretty. After building several applications with JIF, it becomes apparent that they all share much of the same code. Each application goes through the following life cycle:

  1. Construct the jiflet.
  2. Construct the user interface.
  3. Handle events until program ends.

Pretty boring, but such is life when you are only binary information. The pattern that becomes evident to you is that each application has a monotonous bunch of initialization code and a user interface that needs to be copied from application to application. But this base initialization is not in the Jiflet class. Jiflets need to remain pure.

The result is three new abstract classes: SimpleDBJiflet, SimpleDBUI, and DBRecord.

The philosophy is that a SimpleDBJiflet has a user interface that is defined by a SimpleDBUI. They work together to present a pleasant atmosphere for the user. Together, they allow the user to manipulate a single set of database data. This database information is represented by the DBRecord class.

By extending these three abstract classes and basically filling in the blanks, you can create powerful database applications in a matter of hours!

DBRecord

The DBRecord class is the smallest portion of the three new classes. This class represents a single set of database data. The data that is represented by this class is defined in its subclasses; therefore, it is an abstract class. The source code for this class is shown in Listing 12.5.


Listing 12.5. The DBRecord class.
//****************************************************************************
//* Package                                         &nb sp;                        *
//****************************************************************************

package                            jif.sql;

//****************************************************************************
//* Imports                                         &nb sp;                        *
//****************************************************************************

//    JIF imports
import                            jif.sql.*;
import                            jif.awt.*;

//    Java imports
import                             java.sql.*;

//****************************************************************************
//* DBRecord                                         &n bsp;                       *
//****************************************************************************

public abstract class
DBRecord
{

//****************************************************************************
//* Members                                         &nb sp;                        *
//****************************************************************************

    //    An indicator for data changes...
    protected boolean            dataChange = false;
    protected boolean            isNewRecord = false;

//****************************************************************************
//* Constructor                                          ;                     *
//****************************************************************************

    public
    DBRecord()
    {
        clear();
    }

    public
    DBRecord( ResultSet rs )
    {
        parseResultSet( rs );
    }

//****************************************************************************
//* parseResultSet                                        &n bsp;                  *
//****************************************************************************

    /**
    * Parses a "SELECT * ..." result set into itself
    */
    public boolean
    parseResultSet( ResultSet rs )
    {
        isNewRecord = false;
        return( isNewRecord );
    }

//****************************************************************************
//* update                                         &nbs p;                         *
//****************************************************************************

    /**
    * Requests update SQL from the JifPanel and sends it to the database
    * via the DBConnector object passed in.
    */
    public abstract boolean
    update( DBConnector theConnector, JifPanel ap );

//****************************************************************************
//* deleteRow                                         & nbsp;                      *
//****************************************************************************

    /**
    * Constructs delete SQL for the current row
    */
    public abstract boolean
    deleteRow( DBConnector theConnector );

//****************************************************************************
//* setDataChange                                        &nb sp;                   *
//****************************************************************************

    /**
    * Sets a flag indicating that data has changed...
    */
    public boolean
    setDataChange( boolean onOff )
    {
        dataChange = onOff;
        return( dataChange );
    }

//****************************************************************************
//* clear                                           ;                          *
//****************************************************************************

    /**
    * Clears all the variables...
    */
    public void
    clear()
    {
        isNewRecord = true;
        setDataChange( false );
    }


//****************************************************************************
//* canSave                                         &nb sp;                        *
//****************************************************************************

    /**
    * Checks to see if all required fields are filled in
    */
    public boolean
    canSave()
    {
        //    Everything is filled in!
        return( true );
    }

//****************************************************************************
//* didDataChange                                        &nb sp;                   *
//****************************************************************************

    public boolean
    didDataChange()
    {
        return( dataChange );
    }

//****************************************************************************
//* setNewStatus                                        &nbs p;                    *
//****************************************************************************

    public void
    setNewStatus( boolean how )
    {
        isNewRecord = how;
    }

//****************************************************************************
//* getNewStatus                                        &nbs p;                    *
//****************************************************************************

    public boolean
    getNewStatus()
    {
        return( isNewRecord );
    }

}

The DBRecord class can be constructed with or without data. The data required to construct this class is a JDBC ResultSet object. This object is passed to the parseResultSet() method. In your derived class, you must override this method to read the data from the ResultSet into instance variables.

To complete the class, you must provide two additional methods in your derived class: update() and deleteRow(). update() is called to when someone wants you to save the information contained within yourself. deleteRow() is called when someone wants you to delete yourself from the database.

The class provides a clear() method, which you override to clear out your instance variables. This is called, for example, when the user presses the New or Clear buttons.

The DBRecord class has two indicators: dataChange and isNewRecord. These booleans indicate that the data has changed in the record and whether the record exists in the database.

The dataChange value must be manually set and reset. This is done to some degree by the SimpleDBJiflet class. For example, when a record is saved to the database, the dataChange value is set to false. This setting is done by way of the setDataChange() method.

Access methods are provided for you to get at these indicators. getNewStatus() and setNewStatus() allow access to the isNewRecord indicator. setDataChange() and didDataChange() provide access to the dataChange indicator.

Finally, the class provides a method called canSave(). This method returns a Boolean indicating that the record is eligible for saving to the database. This eligibility depends completely on the table that it is representing. This method allows you to validate the data that the user has entered and give it the thumbs up or down.

Note
The DBRecord class is part of the jif.sql package.

A complete example is in order. Listing 12.6 is a complete DBRecord derivation for a table that represents Conference Rooms. This is an actual class created for the Conference Room Scheduling application in this book.


Listing 12.6. A DBRecord subclass.
//****************************************************************************
//* Package                                         &nb sp;                        *
//****************************************************************************

package                            jif.common;

//****************************************************************************
//* Imports                                         &nb sp;                        *
//****************************************************************************

//    JIF imports
import                            jif.sql.*;
import                            jif.awt.*;

//    Java imports
import                             java.sql.*;

//****************************************************************************
//* ConfRoomRecord                                        &n bsp;                  *
//****************************************************************************

/**
* A class that encapsulates a row in the conference room table...
*/
public class
ConfRoomRecord
extends DBRecord
{

//****************************************************************************
//* Constants                                         & nbsp;                      *
//****************************************************************************

    public final static String     TABLE_NAME = "conf_room";

//****************************************************************************
//* Members                                         &nb sp;                        *
//****************************************************************************

    //    A variable for each table column...
    public int                    room_nbr = -1;
    public int                    floor_nbr = -1;
    public String                desc_text = "";

//****************************************************************************
//* Constructor                                          ;                     *
//****************************************************************************

    public
    ConfRoomRecord()
    {
        clear();
    }

    public
    ConfRoomRecord( ResultSet rs )
    {
        parseResultSet( rs );
    }

//****************************************************************************
//* parseResultSet                                        &n bsp;                  *
//****************************************************************************

    public boolean
    parseResultSet( ResultSet rs )
    {
        clear();

        try
        {
            //    Suck out the data...
            room_nbr = rs.getInt( "room_nbr" );
            floor_nbr = rs.getInt( "floor_nbr" );
            desc_text = rs.getString( "desc_text" );
            return( super.parseResultSet( rs ) );
        }
        catch ( SQLException e )
        {
            //    Signal an error...
            clear();
            return( false );
        }
    }

//****************************************************************************
//* update                                         &nbs p;                         *
//****************************************************************************

    /**
    * Requests update SQL from the JifPanel and sends it to the database
    * via the DBConnector object passed in.
    */
    public boolean
    update( DBConnector theConnector, JifPanel ap )
    {
        boolean        success = true;

        try
        {
            //    No update if nothing to do...
            if ( dataChange )
            {
                String    sql;

                //    Generate some SQL!
                if ( getNewStatus() )
                    sql = ap.generateInsertSQL( TABLE_NAME );
                else
                    sql = ap.generateUpdateSQL( TABLE_NAME );

                if ( !sql.equals( "" ) )
                    theConnector.getStatement().executeUpdate( sql );
            }
        }
        catch ( SQLException e )
        {
            theConnector.errorLog( e.toString() );
            success = false;
        }

        return( success );
    }

//****************************************************************************
//* deleteRow                                         & nbsp;                      *
//****************************************************************************

    /**
    * Removes this record from the database...
    */
    public boolean
    deleteRow( DBConnector theConnector )
    {
        boolean        success = true;

        //    Nothing to do...
        if ( getNewStatus() )
            return( false );

        String sql = "delete from " + TABLE_NAME + " where room_nbr " +
            "= " + Integer.toString( room_nbr ) + " and floor_nbr " +
            "= " + Integer.toString( floor_nbr );

        try
        {
            theConnector.getStatement().executeUpdate( sql );
        }
        catch ( SQLException e )
        {
            theConnector.errorLog( e.toString() );
            success = false;
        }

        return( success );
    }

//****************************************************************************
//* clear                                           ;                          *
//****************************************************************************

    /**
    * Clears all the variables...
    */
    public void
    clear()
    {
        super.clear();

        room_nbr = -1;
        floor_nbr = -1;
        desc_text = "";
    }

}

SimpleDBUI

The SimpleDBUI class encapsulates the nonvisual side of the user interface that is necessary for proper application functionality. It extends the JifPanel class providing some default buttons and methods for moving data to and from the user interface components.

The source code for this class is shown in Listing 12.7.


Listing 12.7. The SimpleDBUI class.
//****************************************************************************
//* Package                                         &nb sp;                        *
//****************************************************************************

package                            jif.awt;

//****************************************************************************
//* imports                                         &nb sp;                        *
//****************************************************************************

import                             java.awt.*;

import                            jif.sql.*;
import                            jif.jiflet.*;
import                            jif.common.*;

//****************************************************************************
//* SimpleDBUI                                                                *
//****************************************************************************

public abstract class
SimpleDBUI
extends JifPanel
{

//****************************************************************************
//* Members                                         &nb sp;                        *
//****************************************************************************

    SimpleDBJiflet                 myJiflet;

    //    Some standard buttons...
    public Button                saveButton = new Button( "Save" );
    public Button                clearButton = new Button( "Clear" );
    public Button                newButton = new Button( "New" );
    public Button                deleteButton = new Button( "Delete" );
    public Button                chooseButton = new Button( "Choose" );
    public Button                closeButton = new Button( "Close" );

//****************************************************************************
//* Constructor                                          ;                     *
//****************************************************************************

    public
    SimpleDBUI( SimpleDBJiflet jiflet )
    {
        setJiflet( jiflet );
        setFont( new Font( "Dialog", Font.PLAIN, 12 ) );
    }

//****************************************************************************
//* getJiflet                                         & nbsp;                      *
//****************************************************************************

    public SimpleDBJiflet
    getJiflet()
    {
        return( myJiflet );
    }

//****************************************************************************
//* setJiflet                                         & nbsp;                      *
//****************************************************************************

    public void
    setJiflet( SimpleDBJiflet jiflet )
    {
        myJiflet = jiflet;
    }

//****************************************************************************
//* moveToScreen                                        &nbs p;                    *
//****************************************************************************

    /**
     * Moves data from a DBRecord object to the fields on the screen
     * for editing.
     */
    public abstract void
    moveToScreen();

//****************************************************************************
//* clearScreen                                          ;                     *
//****************************************************************************

    /**
    * Clears the screen fields
    */
    public abstract void
    clearScreen();

//****************************************************************************
//* moveFromScreen                                        &n bsp;                  *
//****************************************************************************

    /**
     * Moves data from the fields on the screen to a DBRecord object.
     */
    public abstract void
    moveFromScreen();

//****************************************************************************
//* action                                         &nbs p;                         *
//****************************************************************************

    public boolean
    action( Event event, Object arg )
    {
        //    Smart JIF components generate ACTION_EVENTs when changed...
        if ( event.target instanceof SQLFactoru )
        {
            //    Notify dad...
            sendJifMessage( event, DATA_chANGE );
            return( true );
        }

        //    User pressed Save...
        if ( event.target == saveButton )
        {
            //    Notify dad...
            sendJifMessage( event, SAVE );
            return( true );
        }

        //    User pressed New...
        if ( event.target == newButton )
        {
            //    Notify dad...
            sendJifMessage( event, NEW );
            return( true );
        }

        //    User pressed Choose
        if ( event.target == chooseButton )
        {
            //    Notify dad...
            sendJifMessage( event, chOOSE );
            return( true );
        }

        //    User pressed Close
        if ( event.target == closeButton )
        {
            //    Notify dad...
            sendJifMessage( event, CLOSE );
            return( true );
        }

        //    User pressed Delete
        if ( event.target == deleteButton )
        {
            //    Notify dad...
            sendJifMessage( event, DELETE );
            return( true );
        }

        //    User pressed Clear
        if ( event.target == clearButton )
        {
            //    Notify dad...
            sendJifMessage( event, CLEAR );
            return( true );
        }

        //    Not handled...
        return( false );
    }

}

Because it is abstract, this class is not very complex. The first thing you notice is that you create a slew of buttons:

public Button                saveButton = new Button( "Save" );
public Button                clearButton = new Button( "Clear" );
public Button                newButton = new Button( "New" );
public Button                deleteButton = new Button( "Delete" );
public Button                chooseButton = new Button( "Choose" );
public Button                closeButton = new Button( "Close" );

These are the standard buttons that the SimpleDBUI knows about. They are defined as public so that you can access them outside of the user interface. Unless they are placed upon a panel or shown in some manner on the screen, they are really never used; therefore, they do not generate messages.

When these buttons are shown on the screen and subsequently pressed by the user, an ACTION_EVENT event is generated. This event is translated into a JifMessage by the action() event handler method. The message is then sent on to the parent, presumably a SimpleDBJiflet, and processed there.

The SimpleDBUI class is expected to move data in and out of a DBRecord class. It does this in three methods: moveToScreen(), moveFromScreen(), and clearScreen(). This class has access to the current DBRecord by way of the jiflet. By calling the SimpleDBJiflet's getDBRecord() method, a reference to the current DBRecord is provided.

moveToScreen() moves data from the DBRecord to the screen components. moveFromScreen() moves data from the screen components to the DBRecord. A clearScreen() clears out the screen components. This last method does not touch the DBRecord really, but a clearScreen() followed by a moveFromScreen() clears out the DBRecord.

Note
The SimpleDBUI class is part of the jif.awt package.

A simple SimpleDBUI derivation is shown in Listing 12.8. This is from the Online In/Out Board application from Chapter 16, "Online In/Out Board."


Listing 12.8. A SimpleDBUI subclass.
//****************************************************************************
//* imports                                         &nb sp;                        *
//****************************************************************************

import                             java.awt.*;

import                            jif.awt.*;
import                            jif.sql.*;
import                            jif.jiflet.*;
import                            jif.common.*;

//****************************************************************************
//* InOutBoardUI                                        &nbs p;                      *
//****************************************************************************

public class
InOutBoardUI
extends SimpleDBUI
{

//****************************************************************************
//* Members                                         &nb sp;                        *
//****************************************************************************

    List                        empList;

//****************************************************************************
//* Constructor                                          ;                     *
//****************************************************************************

    public
    InOutBoardUI( SimpleDBJiflet jiflet )
    {
        super( jiflet );
        setLayout( new BorderLayout() );

        empList = new List();
        empList.setFont( new Font( "Helvetica", Font.BOLD, 14 ) );
        add( "Center", empList );
        empList.enable();

        JifPanel p = new JifPanel();
        p.setLayout( new FlowLayout( FlowLayout.CENTER, 5, 5 ) );
        saveButton.setLabel( "Toggle" );
        saveButton.disable();
        p.add( saveButton );
        add( "South", p );

        //    Set the focus to the first field...
        setFocus( empList );
    }

//****************************************************************************
//* moveToScreen                                        &nbs p;                    *
//****************************************************************************

    /**
     * Moves data from an InOutBoardRecord object to the fields on the screen
     * for editing.
     */
    public void
    moveToScreen()
    {
        if ( getJiflet().getDBRecord() == null )
            return;

        //    Cast one off...
        EmployeeRecord er = ( EmployeeRecord )getJiflet().getDBRecord();

        String s = er.first_name + " " + er.last_name + " is ";

        if ( er.in_out_ind.equalsIgnoreCase( "Y" ) )
            s += "in";
        else
            s += "out";

        empList.addItem( s );
    }

//****************************************************************************
//* clearScreen                                          ;                     *
//****************************************************************************

    /**
    * Clears the record out...
    */
    public void
    clearScreen()
    {
        empList.clear();
    }

//****************************************************************************
//* moveFromScreen                                        &n bsp;                  *
//****************************************************************************

    /**
     * Moves data from the fields on the screen to an EmployeeRecord object.
     */
    public void
    moveFromScreen()
    {
        //    Does nothing…
        return;
    }

}

SimpleDBJiflet

The SimpleDBJiflet class pulls together the DBRecord and SimpleDBUI classes into a cool little hunk of code. This class encapsulates much of the necessary menu and database initialization that must be done for each application.

The SimpleDBJiflet class extends the Jiflet class and adds the following functionality:

Although they are not functional on their own, these features offer quite a bit of legwork that you have to cut and paste from app to app. The beauty of object-oriented programming and Java is that you can stuff all of this functionality into an abstract base class and fill in the blanks. That is all that has been done here.

Note
The SimpleDBJiflet class is part of the jif.jiflet package.

File and Help Menu

The SimpleDBJiflet class creates two menus: a File menu, and a Help menu. The File menu contains two items: Connect and Exit.

The first option, Connect, connects and disconnects the application with the database. This functionality is provided completely as long as the jiflet has a valid DBConnector set for itself.

Note
For more information about the DBConnector class, see Chapter 10, "Database Classes."

After a database connection is established, this menu option changes to Disconnect automatically. When selected, it disconnects your application from the database and changes back to Connect.

Caution
The JDK v1.0.2 for 32-bit Microsoft Windows systems has a bug that prevents a menu item from changing the text after it is displayed. This should be fixed in the JDK v1.1 release.

The second menu option, Exit, disconnects any connected DBConnector and closes the application. This can include writing information to a log file or to the screen. It depends on the configuration of the jiflet.

The Help menu has a single menu item. This item brings up an About box. If you are not familiar with these boxes, they are simply brag boxes for the authors of programs. Some actually show useful information but most just show the program name and a fancy icon along with some copyright information.

Should your jiflet be any different? You're just as proud of your creation as other authors are! Well, set a copyright message with the method setCopyright() and an About box displays automagically! Figure 12.6 shows the About box for the employee maintenance program-nothing too fancy, just the text and a little icon.

Figure 12.6 : The employee maintenance About box.

Tip
A nice extension to the Jiflet class allows custom icons to be associated with the application. Then in their About boxes, the custom icon displays instead of the stock information icon.

The only code required to get that nice About box in the derived Employee program is the following:

setCopyright( "Employee Maintenance v1.00\n" +
    "Copyright (c) 1996 by Jerry Ablan\n" +
    "All Rights Reserved" );

Not a lot of code for such a nice feature! By the way, the About menu item is disabled until you call the setCopyright() method.

Standard Communications: JifMessage

In object-oriented programming, objects communicate with each other by way of messages. But at what point should objects know about the inside workings of other objects? Some purists argue never! Some argue sometimes. However, it is usually a matter of convenience.

While designing many of the applications for this book, I felt that the user interface should be able to manage itself, not knowing about the application driving it. However, I felt that the application needed to know a little about the user interface. Otherwise, you really can't provide any nice bells and whistles. One such feature is to enable and disable the Save button when a data item is modified. This is an excellent visual clue to the user that a change has been made, intentionally or not.

In order to feel politically correct OOP-wise, and to not let my user interface design creep into my application design, I created the JifMessage interface. Listing 12.9 is the source code for the JifMessage interface.


Listing 12.9. The JifMessage interface.
//****************************************************************************
//* Package                                         &nb sp;                        *
//****************************************************************************

package                            jif.jiflet;

//****************************************************************************
//* Imports                                         &nb sp;                        *
//****************************************************************************

import                            java.awt.Event;

//****************************************************************************
//* JifMessage                                                                *
//****************************************************************************

public interface
JifMessage
{

//****************************************************************************
//* Members                                         &nb sp;                        *
//****************************************************************************

    public static final int    NEW = 0;
    public static final int     CLEAR = 1;
    public static final int     SAVE = 2;
    public static final int     DELETE = 3;
    public static final int     CUT = 4;
    public static final int     COPY = 5;
    public static final int     PASTE = 6;
    public static final int     HELP_WINDOW = 7;
    public static final int     HELP_CONTEXT = 8;
    public static final int     HELP_ABOUT = 9;
    public static final int     HELP_HELP = 10;
    public static final int     DATA_chANGE = 11;
    public static final int     chOOSE = 12;
    public static final int     CLOSE = 13;

//****************************************************************************
//* sendJifMessage                                        &n bsp;                  *
//****************************************************************************

    public void
    sendJifMessage( Event event, int msg );

}

Again, it is nothing fancy-simply a list of constants and a consistent method of sending them, which is up to the implementor of this interface. As you can see, many standard actions are represented by the constants in this class: New, Save, Delete, Close, and so on.

After creating this interface, it needs to be implemented somewhere. The JifPanel class is an excellent spot. Because most user interfaces are created with JifPanels, this provides a consistent and standard method of communication with its parent. Listing 12.10 is the code that is added to the JifPanel class to implement this interface.


Listing 12.10. Sending a JifMessage.
//****************************************************************************
//* sendJifMessage                                        &n bsp;                  *
//****************************************************************************

    public void
    sendJifMessage( Event event, int msg )
    {
        event.target = this;
        event.arg = new Integer( msg );
        getParent().postEvent( event );
    }

Nothing too tricky here either. The sendJifMessage() method takes as arguments an event and the message to send. It changes the target of the event to itself (the JifPanel instance) and sets the argument of the event to the message. It then sends the message along to the parent.

Here's a quick example of how it is used. In the SimpleDBUI, there is a built-in Save button. When the user clicks on this button, the class automatically sends a JifMessage.SAVE event to its parent. The parent needs only to listen for these JifMessage events to know what the child wants it to do.

The code for sending from the child looks exactly like this:

//    User pressed Save...
if ( event.target == saveButton )
{
    //    Notify dad...
    sendJifMessage( event, JifMessage.SAVE );
    return( true );
}

The code for receiving in the parent looks something like this:

public boolean
action( Event event, Object arg )
{
    switch ( ( ( Integer )arg ).intValue() )
    {
        case JifMessage.DELETE:
            delDlg = new ResponseDialog( this,
                "Delete Confirmation",
                "Are you sure you want to delete this record?",
                "Yes,No" );

            delDlg.show();
            break;
    }
}

Now that there is a standard way of communicating, you can add some standard features such as saving and deleting.

Record Saving and Deleting

A nice feature for a program to have is a standard method of saving and deleting. Now that you know when the user wants you to save or delete (by way of a JifMessage), you only need some standard methods for doing this.

Enter saveRecord() and deleteRecord(). These two methods provide a method for saving and deleting the information that is stored in the DBRecord of the jiflet. When the SimpleDBUI sends the SAVE or DELETE JifMessage to the jiflet, these methods are called.

They are declared as follows:

//****************************************************************************
//* saveRecord                                                                *
//****************************************************************************

    public boolean
    saveRecord()
    {
        //    If we are not connected, do nothing...
        if ( !getConnector().connected() )
        {
            MessageBox mb = new MessageBox( this, "Hold on there!",
                "You must connect with the database\n" +
                "before you can save any data.\n\n" +
                "Connect first, then try this again!",
                MessageBox.EXCLAMATION );

            mb.show();
            return( false );
        }

        //    Move the data back to the DBRecord...
        getUIPanel().moveFromScreen();

        //    Check to see if all fields are filled in...
        if ( getDBRecord().canSave() )
        {
            //    Save it...
            if ( getDBRecord().update( getConnector(), getUIPanel() ) )
            {
                //    Indicate that it was saved...
                getDBRecord().setDataChange( false );
                setStatus( "Record saved..." );
            }
            else
                setStatus( "Record not saved..." );

            return( true );
        }
        else
        {
            MessageBox mb = new MessageBox( this, "Cannot Save!",
                "All required fields must be entered!",
                MessageBox.EXCLAMATION );

            mb.show();
        }

        return( false );
    }

//****************************************************************************
//* deleteRecord                                        &nbs p;                    *
//****************************************************************************

    public boolean
    deleteRecord()
    {
        //    If we are not connected, do nothing...
        if ( !getConnector().connected() )
        {
            MessageBox mb = new MessageBox( this, "Hold on there!",
                "You must connect with the database\n" +
                "before you can delete any data.\n\n" +
                "Connect first, then try this again!",
                MessageBox.EXCLAMATION );

            mb.show();
            return( false );
        }

        //    Move the data back to the DBRecord...
        getUIPanel().moveFromScreen();

        //    Kill it!
        if ( getDBRecord().deleteRow( getConnector() ) )
        {
            //    Indicate that it was saved...
            getDBRecord().clear();
            getUIPanel().moveToScreen();
            setStatus( "Record deleted..." );
            return( true );
        }
        else
            setStatus( "Record not deleted..." );

        return( false );
    }

This allows you to override them in your derived classes, thus enhancing the functionality. One functionality is to modify two tables instead of one. Or perhaps your jiflet does not save any data but you use the save button and mechanism for some other sort of notification. It is up to you. Be creative!

Data Change Notification

The last function that the SimpleDBJiflet provides is data change notification.

When the SimpleDBUI contains one of the JIF Component extensions (such as JifTextField), it is notified when the user changes the data. This notification is passed along to the SimpleDBJiflet. The SimpleDBJiflet then manages the enabling and disabling of the Save, New, and Delete buttons.

The status depends on the state of the DBRecord at the time. If the record is new, it can't be deleted but it can be saved and cleared (New). If the record has not been changed and is not new, it can be saved, deleted, or cleared. This is not a very complex set of rules but is a hassle to code for each application. You'll find it refreshing when your Save button lights up after you type in your first character.

Summary

This chapter covered every bit of the foundation from the smallest class to the largest. In this chapter, you have an intimate encounter with the Jiflet package. This package combines much of the other packages into a simple, usable application base for creating intranet applications. This application base is the goal set out in Chapter 7, "A Model Intranet Application."

This new base is the foundation for the rest of the book. You see that you create jiflets for each of the sample applications that are discussed in the following chapters.

In Chapter 13, "Employee Files," you discuss the first sample intranet application: Employee Files. This application allows you to maintain your employee information. It's not much, but it is a great starting point for the sample applications.