All Categories :
Java
Chapter 40
Developing Intranet Applications
with Java
by Jerry Ablan
CONTENTS
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.
|
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 a 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 commonalties 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 40.1 is an example of the kind
of configuration files the applications have.
Listing 40.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, then information from a previous session
would 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 even 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 is 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 40.2 shows some sample log entries.
Listing 40.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 40.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.
Note |
JDBC is covered in depth in Chapter 31, "Exploring Database Connectivity with 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 40.3 shows how easy your JDBC database connection class
should be to use.
Listing 40.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 40.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 40.1: The Standard intranet application look and feel.
Referring to Figure 40.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 40.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.
|
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 40.1 shows the package names we have chosen, along
with the class functionality contained within each.
Table 40.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 8, "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
protected ConfigProperties 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 out 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 40.4 shows the complete source code for this constructor.
Listing 40.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 need to call the superclass's constructor with a title.
This is what we do first. Then we do the following:
- 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 40.5 shows the other three constructors available for
creating Jiflet objects.
Listing 40.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 place holder 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, int 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.
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. Listing 40.6 shows the source code for
our smallest jiflet.
Listing 40.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 40.2.
Figure 40.2: The output of SmallJiflet.
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 the JIF, it becomes
apparent that they all share much of the same code. Each application
goes through the following life cycle:
- Construct the jiflet.
- Construct the user interface.
- 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 40.7.
Listing 40.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 for saving 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 40.8 is a complete DBRecord
derivation for a table that represents Conference Rooms.
Listing 40.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 40.9.
Listing 40.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 );
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 );
}
}
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 40.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 40.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.
Caution |
The JDK version 1.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 version 1.1 release.
|
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 40.3 shows
the About dialog box for the employee maintenance program. Nothing
too fancy, just the text and a little icon.
Figure 40.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 40.11 is the source code for the JifMessage
interface.
Listing 40.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 JifPanels, placing the
interface there provides a consistent and standard method of communication
with its parent. Listing 40.12 is the code added to the JifPanel
class to implement this interface.
Listing 40.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 40.13.
Listing 40.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.
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.
Contact
reference@developer.com with questions or comments.
Copyright 1998
EarthWeb Inc., All rights reserved.
PLEASE READ THE ACCEPTABLE USAGE STATEMENT.
Copyright 1998 Macmillan Computer Publishing. All rights reserved.