| ## Java 1.1 Unleashed # - 49 - Developing Intranet Applications with Java by Jerry Ablan IN THIS CHAPTER The Anatomy of an Intranet Application Creating an Intranet Application Framework Programming with Jiflets Extending Jiflets for Real-World Use Now that you know all about Java, you probably want to put it to good use. Writing applets that animate text and graphics on your personal Web pages gets stale after a while. Most likely, you want to build something to show your boss at work. So why not develop some intranet applications? Intranets are the hot new internal webs springing up all over the corporate landscape. Some intranets are broad and diverse while others are skimpy. In addition to providing timely company information such as corporate news and the current price of the company stock, these webs can bring the employees closer. Just like the water cooler of the past, the intranet is the new corporate hangout. But many companies don't even have intranets. Some companies still don't have e-mail. If your company does not yet have an intranet, perhaps developing an application in Java can help get the project started. You never know.... This chapter covers some basic topics regarding intranet applications and Java. In particular, it covers the following areas: The anatomy of an intranet application Putting together an application framework for intranet applications Using the framework to build intranet applications This chapter discusses an intranet application framework developed in the book Developing Intranet Applications with Java (published by Sams.net). The book takes you step-by-step through the design and implementation of an intranet application framework, and then through eight sample intranet applications. NOTE: The framework software developed in Developing Intranet Applications with Java is called the Java Intranet Framework, or JIF. Applications created with JIF are called jiflets. For your convenience, the JIF source code is provided on the CD-ROM that comes with this book. ## The Anatomy of an Intranet Application Intranet applications are like a corporate application suite, which includes word processors, project planners, spreadsheets, and many other useful and productive applications. Intranet applications encompass all departments and touch many types of data. But unlike the business productivity application suites available today, your intranet applications should all share a common foundation. Creating a suite of applications from scratch is tedious and boring. Cutting and pasting code is easy, but that goes against all object-oriented programming practices. What is needed is a basic structure from which you can build your applications. This foundation should be flexible, stable, and extensible. Once developed, it will become your intranet application framework. The next sections introduce you to four standards that all your applications can share. These standards provide a flexible, stable, and extensible foundation for developing intranet applications. Together, these four standards produce a prototype, or model application, you can use as a base when developing other intranet applications. ### A Quick Overview of Intranet Applications On an intranet, the applications that are built share much of the same functionality. Sure, they all do different things, but they do a lot of the same things. These commonalities should become the foundation for your intranet application framework. They are the base and are truly your application standards. Your primary design goal should be to provide a set of standard application features. These features create a familiar atmosphere for all your intranet applications. Familiarity provides users with a sense of comfort because they don't have to learn an entirely new program. Apple Computer capitalized on this idea years ago when it introduced the Macintosh computer. If you learned how to use the Mac, then you knew how to run almost every Macintosh application ever written. It was the consistency and adherence to set standards that made this possible. Microsoft Windows has since capitalized on the same concept. The design presented in this chapter is not quite a Macintosh but it does provides something similar: consistency. The following four standard features are what we suggest you should provide in your intranet application design: Standard configuration file processing Standard logging to screen or disk Standard database connectivity Standard look and feel The following sections examine each feature individually, show an example, and then point you in the right direction to find more information regarding each particular standard. ### Configuration File Processing Using configuration parameters in programming can be a real hassle unless a stable foundation is in place. Generally, you end up coding a new configuration scheme with each application. What you can create is a class that provides a solid method of getting configuration parameters. This method encompasses configuration files on disk and overridden parameters passed in by way of the command line of the application. The configuration file in Listing 49.1 is an example of the kind of configuration files the applications have. #### Listing 49.1. A sample configuration file.
    # Configuration file for Employee Maintenance
    WindowTitle=Employee Maintenance
    server=tcp-loopback.world
    user=munster
    password=
At the start of the application, you can read the configuration file into memory. The parameters can then be merged with any configuration parameters passed in by way of the command line. The applications then need a consistent method of retrieving these parameters from the configuration parameter storage area. A good idea is to model the retrieval method after the method used in regular Java applets. NOTE: In the `jif.util` package, found on the CD-ROM that accompanies this book, is the Java class that implements the design goal of a consistent way to get configuration parameters. It is called `ConfigProperties`. ### Logging to Disk or Screen For tracking problems during the development cycle and for error logging after your application has been deployed, a log file is just the ticket. A common log file is even better for all your applications and users. A common log file is easily searched and filtered for errors. This standard logging mechanism is the first standard feature you should consider developing. To facilitate such a log file, you must create it on disk. You should append to the log file each time; it should never be overwritten. If you overwrite the file, information from a previous session will be lost. You know how annoying that can be! But what if the disk log fails to open, or if it can't be written to? You need a backup log. The failed log entries could go to the screen. This screen logging facility produces the same log information to a window, using the `System.out` facility. When an application fails to create a disk log, all log output can then go to the screen. You can also automatically use this screen log if no disk log is specified or if there is an error creating one. #### Common Log Entries The entries in the log file should follow a standard--a standard that is easy to view and search. This log-file format should be used across all your intranet applications--and possibly your non-intranet applications as well. This log-file format should be simple enough so that other programs may use it. In the future, you may find that you will use a third-party network management tool that can monitor your intranet applications and their log files. These tools can be a lifesaver in a pinch, so why not think about their needs as well? Therefore, the following log-file format provides a good idea of what to go with as the design. It includes all the information needed in an easy-to-use format: application`|`user`|`date`|`level`|`entry In this format, the following fields are used:
Field | Description
application | The name of the application
user | The user who is running the application
date | The date of the log entry
level | A single character that indicates the severity of the entry
The severity levels are up to you to define. As a suggestion however, we offer the following:
Suggested Rating | Meaning | Comment
`D` | Debug | This level is useful for displaying information to the log file during the development stage. When you deploy the application, these messages can easily be filtered from the output.
`I` | Informational | This level is for information that is not important. Startup and shutdown messages are considered informational.
`W` | Warning | This level is for semi-important information. When things don't go exactly as planned, but your program can continue, this is a good place for a warning.
`E` | Error | This level is for important information. When your program encounters any kind of error, this is the level to use.
`F` | Fatal | This level is for very important information. When your program can no longer run because of some event, this is the level to use.
Listing 49.2 shows some sample log entries. #### Listing 49.2. Sample log entries.
    Employee|960909|I|Application [Employee] started at Sun Sep 09 22:27:33 1996
    Employee|960909|I|Server = mars.mcs.net
    Employee|960909|I|Title = Employee Maintenance
    Employee|960909|I|User = munster
    Employee|960909|I|Password = spot
    Employee|960909|I|Application [Employee] ended at Sun Sep 09 22:28:38 1996
At startup, all your intranet applications should write several things to the log: A startup message showing the application and time/date of startup Optionally, the contents of the configuration file Optionally, any arguments passed in on the command line At shutdown, your application can write a corresponding entry to its startup message. Refer back to Listing 49.2 for examples of shutdown entries. NOTE: In the `jif.log` package, found on the CD-ROM that accompanies this book, are the Java classes that implement the design goal of consistent, or common, log-file formatting. They are called `DiskLog` and `ScreenLog`. ### Database Connectivity Connecting to databases with Java is a key point in your intranet application design stage. Various methods are currently available. Some are HTTP server extensions that return data in HTML format. Others are nonportable system-dependent solutions. A more Java-like option is JDBC. It appears today that JDBC is the strongest supported database standard for Java. For this reason alone, JDBC has been chosen as the database connectivity package for the intranet application in this chapter. Using JDBC allows us to choose from almost any database and provides the flexibility to change databases once coding is complete. To simplify database connectivity just a bit, we can create a class that encapsulates the more monotonous aspects of connecting and disconnecting from a database server. This new class provides a connection strategy that is simple to use and easily extensible. This class also encapsulates much of the rudimentary JDBC initialization and cleanup. All we have to do is extend this class for each database we need to connect with. Listing 49.3 shows how easy your JDBC database connection class should be to use. #### Listing 49.3. How we want to use our database connector class.
    // Make the connection...
    if ( myConnector.connect( "username", "password", "dbservername" ) )
    {
    // Connection successful...
    }
    else
    {
    // Connection failed...
    }
As you can see, the `connect()` method is called with the connection parameters necessary to make the connection. The exception handling is handled for you in the class, returning nothing but a simple `true` or `false`. This return value indicates the connection state as well. NOTE: On the CD-ROM that accompanies this book, in the `jif.sql` package, are the Java classes that implement the design goal of consistent database connectivity. The `DBConnector` class is the abstract base for many of the database classes. Also provided in the `jif.sql` package on the CD-ROM are classes that connect to Oracle, Sybase, Microsoft SQL Server, ODBC, and mSQL. ### Look and Feel The final standard your intranet applications should follow is that they should have a consistent look and feel. This is achieved through the use of standard font and a consistent component layout. Figure 49.1 shows the standard look and feel of the model intranet application. This application is the ever-present and highly overrated `Hello World` example. FIGURE 49.1. The standard intranet application look and feel. Referring to Figure 49.1, you see the following standard application attributes: A standard Java font. We choose Dialog, size 12. In Windows 95, the standard font is 8-point MS Sans Serif. A status bar. The status bar is along the bottom and is used to display messages to the user. You may notice that there is a menu option shown in Figure 49.1. However, no menu standard is included as part of the standard look and feel for intranet applications. Because the menu varies from application to application, it is not fair to impose a rigid menu structure on applications that may not even need a menu. Therefore, menus are not part of the application design. NOTE: On the CD-ROM that accompanies this book, in the `jif.awt` package, are the Java classes that implement the design goal of a consistent look and feel. The `StatusBar` class in particular provides a single-line text output area for your applications. There are many other user interface classes in the `jif.awt` package as well. These can all be used to enhance and beautify your intranet applications. ## Creating an Intranet Application Framework Now you have an idea of the types of building blocks you can use in your intranet applications. We've covered four standard features that can create a sense of consistency for you and your users. Now we can take these features and mold them into an application framework. An application framework is, in its simplest form, an application that does nothing. However, a better definition is this: An application framework provides developers with the necessary foundation from which they can build solid applications. NOTE: Those of you familiar with Borland C++ or Visual C++ are aware of the benefits of a foundation framework. Using the tools provided by Borland C++ or Visual C++, you can generate a complete application shell in minutes. You, the programmer, are left with the job of providing content for the application. The tedious parts like printing and window management are already done for you. Application frameworks are one of the better advances in developer technology in years. We'll name our framework the Java Intranet Framework because it provides a framework for creating intranet applications with Java. Not to mention the cool acronym: JIF! ### How to Package the Application Framework The best way to package the application framework is in groups of functionality. As you recall, we have four functionality groups: utilities, logging, database, and user interface. These fit perfectly into four functionality packages. Naming our packages is quite simple: We use `jif` as our base package name and then add on the specifics. For example, the utilities package is called `util`, similar to the `java.util` package. Table 49.1 shows the package names we have chosen, along with the class functionality contained within each. #### Table 49.1. The Java Intranet Framework packages.
Package Name | Description
`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. TIP: Placing Java classes into packages is simple. In each file, add a line of code that defines the package to which the class belongs. These statements are `package` statements as discussed in Chapter 5, "Classes, Packages, and Interfaces." In addition to adding the `package` statements to your source code, you must also move all the classes into a directory hierarchy that represents the package hierarchy. ### Constructing the Foundation Now that we're headed toward building a framework, we need to use our classes to construct our foundation so that we can build intranet applications. What we really need is something to model our framework on. 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. ### Making the Framework Easy to Use To make our jiflets easy to work with, we've modeled them after Java's own `Applet` class. The jiflet is a Java application that provides the standard features of our intranet application in an easy-to-use wrapper. These features include all four 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. The `init()` method is a centralized location in which you can place all your initialization code. Another feature our jiflet should contain is a standard way to get configuration parame- ters. We can retrieve parameters from the configuration file with a method like the `java.Applet.getParameter()` method. In fact, the following methods are available both in applets and jiflets: `public void init()` `public boolean isActive()` `public void destroy()` `public String getParameter( String` name`)` `public void resize(Dimension d)` `public void resize(int` width`, int` height`)` `public void showStatus(String` msg`)` The only features really missing from jiflets that appear in applets are the multimedia methods. These multimedia 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 the implementation of the classes. ### The Jiflet Class The `Jiflet` class is the intranet application version of Java's own `Applet` class. It provides you with a framework to develop intranet applications quickly and easily. It brings together all the usefulness of the other JIF packages into a single, easy-to-use component. Let's take a look at the `Jiflet` class in detail. We start with the instance variables, 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: how to use it and how to use it to develop intranet applications with Java. #### 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;
Let's look at each one and how it is used. #### activeFlag
    protected boolean activeFlag = false;
The `activeFlag` variable is used to denote the activeness of a jiflet. Its state can be queried with the `Jiflet.isActive()` method. The `activeFlag` variable 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` variable is the log file object for the jiflet. During construction, this object is created like so:
    appLogFile = new DiskLog( logPath, DiskLog.createLogFileName(), appName );
In this syntax, `logPath` is a configurable location in which all log files are placed. The `DiskLog.createLogFileName()` method creates a standard log filename. Finally, `appName` is used on the standard log entry to identify the application that generates it. #### appName
    protected String appName;
The `appName` string holds the name of the application that is running. This string 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 the `appVerbosity` boolean instance variable. It defaults to `false` and can be set with the `Jiflet.setVerboseMode()` method. #### configProperties
    protectedConfigProperties configProperties;
The `configProperties` 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;
The `defaultLog` variable is a default log in case the real application log cannot be created. This log writes its entries to the standard output device of the operating system. #### myConnector
    private DBConnector myConnector = null;
The `myConnector` variable holds the `DBConnector` object associated with this jiflet. This variable can be set and retrieved with the `Jiflet.setConnector()` and `Jiflet.getConnector()` methods. #### myStatusBar
    protected StatusBar myStatusBar = null;
The `myStatusBar` variable holds the instance of the `StatusBar` object created for the jiflet. The status can be set and cleared with the `Jiflet.showStatus()` and `Jiflet.clearStatus()` methods. #### oldCursor
    private int     oldCursor = -1;
The `oldCursor` 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. Each of these four constructors is 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. Listing 49.4 shows the complete source code for this constructor. #### Listing 49.4. The jiflet's master 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, we first need to call the superclass's constructor with a title. Then the following is done: The background color is set to light gray. The jiflet is centered on the screen. The status bar is created and enabled. The default log is opened. The configuration file is read into memory and combined with the program arguments. The `LogPath` configuration option is retrieved from the configuration. The actual disk log file is created. Verbose mode is turned on or off depending on the value passed in. A message is written to the log, stating that the jiflet has started. The user's implemented `init()` method is called. The `run()` method is called. By default, the `show()` method of the `Frame` is called to display the jiflet on the screen. Listing 49.5 shows the other three constructors available for creating `Jiflet` objects. #### Listing 49.5. Three more constructors 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, all three of these constructors call the main constructor, setting some values to `null` or blanks. #### Methods Many methods are available in the `Jiflet` class. The following sections document their arguments and what purpose they serve. #### setVerboseMode
    () public void
    setVerboseMode( boolean whichWay )
The `setVerboseMode()` 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: The `setVerboseMode()` method, in conjunction with `Jiflet.verboseLog()`, offers an excellent debugging tool. Simply enable verbose mode when you need more detail; 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 )
The `verboseLog()` 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 *
    //****************************************************************************
>     public void
>     errorLog( String logEntry )
The `errorLog()` method allows you to create error log entries without specifying an error logging level each time. It is simply a convenience method. ### log()
    //****************************************************************************
    //* log *
    //****************************************************************************
>     public void
>     log( char logLevel, String logEntry )
>     public void
>     log( String logEntry )
The `log()` 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 )
The `handleEvent()` method overrides the default `Frame.handleEvent()` method. It listens for destruction events so that the jiflet can close itself down cleanly. ### action()
    //****************************************************************************
    //* action *
    //****************************************************************************
>     public boolean
>     action( Event event, Object arg )
The `action()` method receives `ACTION_EVENT` events from the event system. It listens for menu events and passes them to `Jiflet.handleMenuEvent()`. ### handleMenuEvent()
    //****************************************************************************
    //* handleMenuEvent *
    //****************************************************************************
>     protected boolean
>     handleMenuEvent( Event event, Object arg )
The `handleMenuEvent()` method is a placeholder for menu events. This method does nothing in the `Jiflet` class. It must be overridden by derived classes to include any functionality. ### shutDown()
    //****************************************************************************
    //* shutDown *
    //****************************************************************************
    public boolean
    shutDown( int level )
The `shutDown()` method is the central point of exit for the jiflet. With the exception of a program crash, the jiflet always exits through this method. The method 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 *
    //****************************************************************************
>     public void
>     suicide( Exception e, String logLine, int level )
>     public void
>     suicide( String logLine )
>     public void
>     suicide( String logLine, nt level )
>     public void
>     suicide( Exception e )
>     public void
>     suicide( Exception e, String logLine )
The `suicide()` method allows a jiflet to gracefully kill itself. Depending on the circumstances, you may or may not want a log entry written, and you may or may not have an exception that caused your program's death. ### center()
    //****************************************************************************
    //* center *
    //****************************************************************************
>     public void
>     center()
The `center()` method centers the jiflet window on the screen. ### enableStatusBar()
    //****************************************************************************
    //* enableStatusBar *
    //****************************************************************************
>     public void
>     enableStatusBar( String text )
>     public void
>     enableStatusBar()
The `enableStatusBar()` method creates a status bar with or without text and adds it to the jiflet's layout. ### clearStatus()
    //****************************************************************************
    //* clearStatus *
    //****************************************************************************
>     public void
>     clearStatus()
The `clearStatus()` method clears the text in the status bar. ### showStatus()
    //****************************************************************************
    //* showStatus *
    //****************************************************************************
>     public void
>     showStatus( String text )
The `showStatus()` method sets the text in the status bar. ### setConnector()
    //****************************************************************************
    //* setConnector *
    //****************************************************************************
>     protected void
>     setConnector( DBConnector aConnector )
The `setConnector()` method sets the `DBConnector` object associated with this jiflet. ### getConnector()
    //****************************************************************************
    //* getConnector *
    //****************************************************************************
>     public DBConnector
>     getConnector()
The `getConnector()` method returns the previously associated `DBConnector` object to the caller. ### startWait()
    //****************************************************************************
    //* startWait *
    //****************************************************************************
>     public void
>     startWait()
The `startWait()` method is a luxury method. It changes the cursor to the system default "wait" cursor. Usually an hourglass, this cursor indicates that a lengthy process is occurring. ### endWait()
    //****************************************************************************
    //* endWait *
    //****************************************************************************
>     public void
>     endWait()
The `endWait()` method returns the cursor to its previous state if called after a `Jiflet.startWait()` call. Otherwise, this method does nothing. ### getParameter()
    //****************************************************************************
    //* getParameter *
    //****************************************************************************
>     public String
>     getParameter( String key )
>     public String
>     getParameter( String key, String defaultValue )
The `getParameter()` 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 *
    //****************************************************************************
>     public boolean
>     canClose()
The `canClose()` method is called before the jiflet is allowed to close. It provides you with 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 they exit to see whether they have 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 *
    //****************************************************************************
>     public boolean
>     isActive()
The `isActive()` method returns `true` if the jiflet is currently active; otherwise it returns `false`. The `isActive()` method is similar to the `Applet.isActive()` method. A jiflet is considered active right before its `run()` method is called. ### Wrapping Up Jiflets Now that we have this new `Jiflet` class, we need to place it somewhere. Again we borrow from the example set by Sun and place it in a package called `jif.jiflet`. This package contains the `Jiflet` class. ## Programming with Jiflets Now that we've created a class that implements the design goals, let's take it for a test drive. Remember that because we've modeled our jiflet on Java's `Applet` class, creating a jiflet should be quite simple. ### The Smallest Jiflet The smallest possible jiflet simply prints a string on the screen and does nothing else. List- ing 49.6 shows the source code for our smallest jiflet. #### Listing 49.6. The smallest jiflet.
    //****************************************************************************
    //* Imports *
    //****************************************************************************
    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 *
    //****************************************************************************
       public void
       run()
       {
             show();
       }
    }
The `main()` method is called by the Java interpreter to run your program. It is required to create the class that is your program. It is in the `main()` method that we create an instance of `SmallJiflet`. The `pack()` method readjusts the size of the jiflet to accommodate all the user interface objects contained within it. The `pack()` method is necessary because, unlike an applet, we have no predefined width or height. The output of our small jiflet is shown in Figure 49.2. FIGURE 49.2. The output of `SmallJiflet`. ## Extending Jiflets for Real-World Use Now that you have a basic understanding of the Java Intranet Framework (JIF), the rest of this chapter extends the jiflet concept into a form that can be easily 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 using 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 the 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 must be copied from application to application. But this base initialization is not in the `Jiflet` class. Jiflets have to remain pure. The result is three new abstract classes: `SimpleDBJiflet`, `SimpleDBUI`, and `DBRecord`. The philosophy is that a `SimpleDBJiflet` has a user interface defined by a `SimpleDBUI`. The two 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 filling in the blanks as it were), you can create powerful database applications in a matter of hours! ### The DBRecord Class The `DBRecord` class is the smallest of the three new classes. This class represents a single set of database data. The data represented by this class is defined in its subclasses, therefore `DBRecord` is an abstract class. The source code for this class is shown in Listing 49.7. #### Listing 49.7. The DBRecord class.
    //****************************************************************************
    //* Package *
    //****************************************************************************
    package                       jif.sql;
    //****************************************************************************
    //* Imports *
    //****************************************************************************
    //    JIF imports
    import                        jif.sql.*;
    import                        jif.awt.*;
    // Java imports
    import                        java.sql.*;
    //****************************************************************************
    //* DBRecord *
    //****************************************************************************
    public abstract class
    DBRecord
    {
    //****************************************************************************
    //* Members *
    //****************************************************************************
       // An indicator for data changes...
       protected boolean dataChange = false;
       protected boolean isNewRecord = false;
    //****************************************************************************
    //* Constructor *
    //****************************************************************************
        public
        DBRecord()
        {
            clear();
        }

        public
        DBRecord( ResultSet rs )
        {
            parseResultSet( rs );
        }
    //****************************************************************************
    //* parseResultSet *
    //****************************************************************************
        /**
        * Parses a "SELECT * ..." result set into itself
        */
        public boolean
             parseResultSet( ResultSet rs )
        {
        isNewRecord = false;
        return( isNewRecord );
        }
    //****************************************************************************
    //* update *
    //****************************************************************************
        /**
        * 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 *
    //****************************************************************************
        /**
        * Constructs delete SQL for the current row
        */
        public abstract boolean
        deleteRow( DBConnector theConnector );
    //****************************************************************************
    //* setDataChange *
    //****************************************************************************

        /**
        * 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 *
    //****************************************************************************

        /**
        * Checks to see if all required fields are filled in
        */
        public boolean
        canSave()
        {
            // Everything is filled in!
            return( true );
        }
    //****************************************************************************
    //* didDataChange *
    //****************************************************************************
        public boolean
        didDataChange()
        {
            return( dataChange );
        }
    //****************************************************************************
    //* setNewStatus *
    //****************************************************************************
        public void
        setNewStatus( boolean how )
        {
           isNewRecord = how;
        }
    //****************************************************************************
    //* getNewStatus *
    //****************************************************************************
        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 in 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 when someone wants you to save the information that your class contains. `deleteRow()` is called when someone wants you to delete the information your class contains. The class provides a `clear()` method, which you override to clear out your instance variables. The `clear()` method is called, for example, when the user presses the New or Clear button. The `DBRecord` class has two indicators: `dataChange` and `isNewRecord`. The `boolean` value `dataChange` indicates that the data has changed in the record; the `boolean` value `isNewRecord` indicates 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 made 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` value indicating whether the record is eligible to be saved to the database. Eligibility depends completely on the table the record represents. The `canSave()` 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, included on the CD-ROM that accompanies this book. A complete example is in order. Listing 49.8 is a complete `DBRecord` derivation for a table that represents Conference Rooms. #### Listing 49.8. A DBRecord subclass.
    //****************************************************************************
    //* Package *
    //****************************************************************************
    package                        jif.common;
    //****************************************************************************
    //* Imports *
    //****************************************************************************
    //    JIF  imports
    import                        jif.sql.*;
    import                        jif.awt.*;
    //    Java imports
    import                        java.sql.*;
    //****************************************************************************
    //* ConfRoomRecord *
    //****************************************************************************
    /**
    * A class that encapsulates a row in the conference room table...
    */
    public class
    ConfRoomRecord
    extends DBRecord
    {
    //****************************************************************************
    //* Constants *
    //****************************************************************************
        public final static String TABLE_NAME = "conf_room";
    //****************************************************************************
    //* Members *
    //****************************************************************************
         // 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 *
    //****************************************************************************
        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 *
    //****************************************************************************
        /**
        * 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 *
    //****************************************************************************
       /**
       * 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 = "";
        }
    }
### The SimpleDBUI Class The `SimpleDBUI` class encapsulates the nonvisual side of the user interface that is necessary for proper application functionality. The class extends the `JifPanel` class by 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 49.9. #### Listing 49.9. The SimpleDBUI class.
    //****************************************************************************
    //* Package *
    //****************************************************************************

    package            jif.awt;

    //****************************************************************************
    //* imports *
    //****************************************************************************

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

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

    public abstract class
    SimpleDBUI
    extends JifPanel
    {

    //****************************************************************************
    //* Members *
    //****************************************************************************

       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 *
    //****************************************************************************

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

    //****************************************************************************
    //* setJiflet *
    //****************************************************************************

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

    //****************************************************************************
    //* moveToScreen *
    //****************************************************************************

        /**
        * 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 *
    //****************************************************************************

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

    //****************************************************************************
    //* action *
    //****************************************************************************

        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 );
            }

                // 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 );
         }
    }
Being abstract, the `SimpleDBUI` class is not very complex. The first thing you notice about the class is that we 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 the user interface. Unless they are placed on 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 clicked 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 using 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. The `moveToScreen()` method moves data from the `DBRecord` to the screen components. The `moveFromScreen()` method moves data from the screen components to the `DBRecord`. And `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, included on the CD-ROM that accompanies this book. A simple `SimpleDBUI` derivation is shown in Listing 49.10. This is from the Online In/Out Board application. This application is provided for you on the CD-ROM that accompanies this book. #### Listing 49.10. A SimpleDBUI subclass.
    //****************************************************************************
    //* imports *
    //****************************************************************************
    import             java.awt.*;
    import             jif.awt.*;
    import             jif.sql.*;
    import             jif.jiflet.*;
    import            jif.common.*;
    //****************************************************************************
    //* InOutBoardUI *
    //****************************************************************************
    public class
    InOutBoardUI
    extends SimpleDBUI
    {
    //****************************************************************************
    //* Members *
    //****************************************************************************
        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 *
    //****************************************************************************
        /**
        * 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 *
    //****************************************************************************
        /**
        * Moves data from the fields on the screen to an EmployeeRecord object.
        */
        public void
        moveFromScreen()
        {
                // Does nothing...
                return;
        }
    }
### The SimpleDBJiflet Class 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: A File menu with standard database connectivity A Help menu with a working About dialog box A standard method of communicating with the user interface Record saving and deleting Data modification notification Although they are not functional on their own, these features keep you from doing the legwork of cutting and pasting from app to app. The beauty of object-oriented programming and Java is that you can stuff all 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, included on the CD-ROM that accompanies this book. #### The File and Help Menus 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 to and from the database. This functionality is provided completely as long as the jiflet has a valid `DBConnector` set for itself. After a database connection is established, the Connect menu option changes to Disconnect automatically. When the Disconnect option is selected, it disconnects the application from the database and the option changes back to Connect. The second menu option, Exit, disconnects any connected `DBConnector` and closes the application. The Exit option 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 that brings up an About dialog box. If you are not familiar with these critters, they are simply brag boxes for the authors of programs. Some of these dialog boxes 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 dialog box displays automagically! Figure 49.3 shows the About dialog box for the employee maintenance program. Nothing too fancy, just the text and a little icon. FIGURE 49.3. The employee maintenance About dialog box. TIP: A nice extension to the `Jiflet` class allows custom icons to be associated with the application. Then in their About dialog 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. It is usually, however, 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, because it doesn't know 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. 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 49.11 is the source code for the `JifMessage` interface. #### Listing 49.11. The JifMessage interface.
    //****************************************************************************
    //* Package *
    //****************************************************************************
    package             jif.jiflet;
    //****************************************************************************
    //* Imports *
    //****************************************************************************
    import             java.awt.Event;
    //****************************************************************************
    //* JifMessage *
    //****************************************************************************
    public interface
    JifMessage
    {
    //****************************************************************************
    //* Members *
    //****************************************************************************
        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 *
    //****************************************************************************
        public void
        sendJifMessage( Event event, int msg );
    }
Again, the `JifMessage` interface 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, we have to implement it somewhere. I felt that the `JifPanel` class is an excellent spot. Because most user interfaces are created with `JifPanel`s, placing the interface there provides a consistent and standard method of communication with its parent. Listing 49.12 is the code added to the `JifPanel` class to implement this interface. #### Listing 49.12. Sending a JifMessage.
    //****************************************************************************
    //* sendJifMessage *
    //****************************************************************************
        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` class, there is a built-in Save button. When the user clicks 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 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, we can add some standard features like 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 we know when the user wants us to save or delete (by way of a `JifMessage`), we need some standard methods for doing this. Enter the `saveRecord()` and `deleteRecord()` methods. These two methods provide a way to save and delete the information stored in the `DBRecord` of the jiflet. When the `SimpleDBUI` sends the `SAVE` or `DELETE` `JifMessage` to the jiflet, one of these methods is called. The methods are declared as shown in Listing 49.13. #### Listing 49.13. Declaring the saveRecord() and deleteRecord() methods.
    //****************************************************************************
    //* 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 *
    //****************************************************************************
        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 );
    }
Having these methods in the base class 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` class provides is data change notification. When the `SimpleDBUI` class 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` class. The `SimpleDBJiflet` class then manages the enabling and disabling of the Save, New, and Delete buttons. The status of the record 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 or 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 it is a hassle to code for each application. You'll find it refreshing when your Save button lights up after you type your first character. ## Summary This chapter thoroughly covered an intranet application framework. You had an intimate encounter with the `Jiflet` class, which is part of the Java Intranet Framework. This base, or framework, is provided for you on the CD-ROM that accompanies this book. You can use it to create your own intranet applications with Java.
| ©Copyright, Macmillan Computer Publishing. All rights reserved.