Chapter 16

Online In/Out Board


CONTENTS


lunch \lunch\ n:a meal eaten at midday

Introduction

Welcome to your fourth sample intranet app. Lighter on the complexity side than the last one, this program utilizes the existing employee table to provide an Online In/Out Board. You might not be familiar with the concept. Many companies use a chalkboard or whiteboard with each employee's name on it. Next to the name of the employee is his or her location, indicating whether he or she is in or out of the office. These boards are commonly placed where administrative assistants can easily see who is in or out. There is an online version that is representative of the same concept. This chapter will cover the following topics in regard to the Online In/Out Board app:

This four step format will be used throughout all of the sample app chapters. Hopefully, it will provide you with valuable insight and ideas for creating your own intranet apps.

app Design

This app is one of the simpler apps in the tutorial. Once the app has been connected to a data source, it will present the user with a list of all the employees on file. In addition, their current location, in or out, will be shown. Screenshot is the proposed user interface for the Online In/Out Board program. Screenshot : The Online In/Out Board user interface. The interface will utilize a List component to display employees, and a single Toggle button will toggle the employee's status. Once the list has been presented, the user may select an employee and press the Toggle button. This button toggles the current in/out indicator from yes to no, or vice versa, depending on the original value of the indicator. The Toggle button will not become enabled until an employee has been chosen. This visually informs the user that he or she can then do something with his or her selection. Screenshot shows the app with a selection made. Screenshot : The Online In/Out Board with the employee Karen Kenny selected. In addition to pressing the Toggle button, the user will be able to double-click the selection to achieve the same effect. (See Figure 16.3). Screenshot : The Online In/Out Board after we've toggled Mr. Kenny out. Finally, the list needs to be refreshed periodically. A timer should be used to automatically refresh the list at a configurable period of time. The default is 60 seconds. You can override this default value by placing a refresh.rate property into your configuration file. 60 seconds is probably an optimal setting. If you set it any lower, you'll just be clogging your network and database with unnecessary requests. If you go higher, you may miss out on changes at lunch- time.

Database Design

This app utilizes the employee table that was defined in , "Employee Files." It toggles the in_out_ind column value from Y to N, and vice versa. For your convenience, the layout for the employee table is shown in Table 16.1.

Table 16.1 Layout for Employee Table
Description Column Name Type Null?
Default
Employee ID emp_id number( 5 ) N
None
First Name first_name char( 40 ) N
None
Middle Name mid_name char( 40 ) Y
None
Last Name last_name char( 40 ) N
None
Social Security Number ssn char( 15 ) Y
None
Address Line 1 addr_line_1 char( 80 ) Y
None
Address Line 2 addr_line_2 char( 80 ) Y
None
City city char( 80 ) Y
None
State state char( 80 ) Y
None
Zip Code zip_code char( 20 ) Y
None
Salary salary number( 7,2 ) Y
None
Home Phone Number home_phone_nbr char( 20 ) Y
None
Work Extension Number work_ext_nbr char( 20 ) Y
None
In/Out Indicator in_out_ind char( 1 ) N
'N'

Implementation

In the rest of this chapter I will discuss the implementation of the Online In/Out Board program. I'll first discuss the user interface and how it was created. Secondly, I'll discuss the database access used in the program. Finally, I'll cover any coding pitfalls that came up during the app construction. Each sample app in this tutorial uses a different approach to developing the user interface. This variety will show you the different ways you can go about doing your own interfaces. Hopefully, you will get a nice cross-section of many different styles and choose the one that suits you the best.

User Interface

To achieve the design goal presented above, you do not need special user interface components; the stock BorderLayout is sufficient. You'll also employ the List class and a Button. The following is the user interface construction code for the Employee program:

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

List empList;

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

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

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

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

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

First, set the layout to a new BorderLayout. The List component is created and placed in the center of the layout. This is your employee list. Referring to Figure 16.1, you'll see that this list expands on all sides to fill the space.

Caution
The default layout for the JifPanel class is FlowLayout. Because the SimpleDBUI class extends the JifPanel class, its default layout is also the FlowLayout. Therefore, if you want a different layout, you must create it and set it here.

Your Toggle button is next. In order to get the automatic record saving mechanism to work in your favor, I'll rename the Save button to Toggle. This button is also disabled. Overriding the saveRecord() method and placing any customized row saving codes in it frees you from monitoring for special events, or even a new button's events. For example, if you want to change the Save button's name to something like Play, you could then override the saveRecord() method to receive notification of this being clicked. I'll cover this in the database access section later in this chapter. Screenshot illustrates the layout of this app. Screenshot : The layout of the Online In/Out Board.

Handling the Toggle Button

You want your Toggle button to enable, or light up, when the user has made a selection. This is easily done by looking for the correct events. In your handleEvent() method, use the LIST_SELECT and LIST_DESELECT events to enable and disable the button:

//****************************************************************************
//* handleEvent &nbs p; *
//****************************************************************************

public boolean
handleEvent( Event event )
{
// Turn on/off buttons...
if ( event.target instanceof List )
{
switch ( event.id )
{
case Event.LIST_SELECT:
getUIPanel().saveButton.enable();
return( true );

case Event.LIST_DESELECT:
getUIPanel().saveButton.disable();
return( true );
}
}

return( super.handleEvent( event ) );
}

Your Toggle button is really the Save button in sheep's clothing: You change the text on it to say Toggle. Changing the text does not alter its behavior. It still generates JifMessage.SAVE app messages in your framework. It still generates these messages because, instead of checking the text of the button when the initial ACTION_EVENT event is generated, you check the event's target with the member instance variables for all the buttons that you created in your base SimpleDBUI class. Checking the text is a potentially unreliable way to match commands.

Tip
Try not to rely on the text of a component to identify which component it is. This practice is somewhat unreliable and can be misleading. Also, if someone comes along later and changes your program, they could introduce a new component with duplicate text. This could potentially harm your program. Be careful!

What does this all mean to you? It means you can reliably change the text of the Save button. When this occurs, you will receive notification of the event by having your saveRecord() method called. To handle the Toggling of the selected employee, you will utilize this saveRecord() method:

//****************************************************************************
//* saveRecord ; *
//****************************************************************************

public boolean
saveRecord()
{
return( toggleListItem( ( ( InOutBoardUI )getUIPanel() ).empList ) );
}

Retrieve the user interface panel's empList variable and pass it to your Toggling function for processing. The result is returned.

Capturing Double-Clicks

In addition to the user pressing the Toggle button, you want the user to be able to double-click the mouse on an employee. This double-clicking will toggle the in/out status of that employee. This is handled in your action() method. When a List component receives a double-click, it generates an ACTION_EVENT event with itself as the target. What you need to do is capture this event. Call your Toggling routine:

// If list was double-clicked
if ( event.target instanceof List )
return( toggleListItem( ( List )event.target ) );

The List is the target of the event, therefore you can simply pass it along to your Toggling method.

Database Access

This program reuses the EmployeeRecord object that was introduced in . It is a versatile class that represents a single row in the employee table. This DBRecord derivation knows how to create, read, update, and delete records from the employee table.

Note
The EmployeeRecord and other database classes are reused in several other apps. They have been placed in their own package along with other shared code. This package is called jif.common. It contains all the common classes between all the apps.

There are two main database access areas to this program: the initial status retrieval and the storage of new statuses. I'll examine each one individually.

Retrieving All the Statuses

At startup, and during the lifetime of the app, the database needs to be queried, and the results displayed for the user. These results are massaged data columns taken from the database. Retrieve the status of all employees for display:

//****************************************************************************
//* loadPanel &nbs p; *
//****************************************************************************

public void
loadPanel()
{
// Make sure we're cool to go...
if ( getConnector() == null || !getConnector().connected() )
return;

String sql = "select * from emp order by last_name";

// Clear out the old stuff...
getUIPanel().clearScreen();

try
{
if ( getConnector().getStatement().execute( sql ) )
{
ResultSet rs = getConnector().getStatement().getResultSet();

int row = 0;

while ( rs.next() )
{
EmployeeRecord er = new EmployeeRecord( rs );
setDBRecord( er );
getUIPanel().moveToScreen();

// Keep a map...
rowMap.insertElementAt( er, row );
row++;
}
}
} catch ( SQLException e )
{
errorLog( "Error during loading: " + e.toString() );
}

return;
}

This method issues an SQL query that returns all of the employees in your employee table. Each returned row is stored into an EmployeeRecord object. The stored row is then placed into a Vector for later use. Notice that the employee list is never populated here because you've placed that code in the moveToScreen() method of your user interface class. It is only there that data is moved to the screen. Before it can move data to your list, though, you need to tell the base class which DBRecord to use. This is done with the call to setDBRecord(). The moveToScreen() code is simple:

//****************************************************************************
//* moveToScreen & nbsp; *
//****************************************************************************

public void
moveToScreen()
{
if ( getJiflet().getDBRecord() == null )
return;

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

String s = er.first_name.trim() + " " + er.last_name.trim() + " is ";

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

empList.addItem( s );
}

Here, you retrieve the EmployeeRecord from your jiflet, concatenating the first and last names with a string representing their present location-in or out.

Toggling an Employee's Status

Once an employee has been selected, and the intention to toggle his or her status has been made clear, the toggleListItem() method comes into play:

//****************************************************************************
//* toggleListItem ; *
//****************************************************************************

public boolean
toggleListItem( List theList )
{
String newInOut;
int idx = theList.getSelectedIndex();
String si = theList.getSelectedItem();

// Break down the selection...
StringTokenizer st = new StringTokenizer( si, " " );

String first_name = st.nextToken();
String last_name = st.nextToken();
String skip_me = st.nextToken();
String in_out = st.nextToken();

// Rebuild the string...
si = first_name.trim() + " " + last_name.trim() + " is ";

if ( in_out.equals( "in" ) )
{
si += "out";
newInOut = "N";
}
else
{
si += "in";
newInOut = "Y";
}

try
{
// Try and save it...
String sql = "update emp set in_out_ind = '" + newInOut + "' " +
"where emp_id = " +
( ( EmployeeRecord )rowMap.elementAt( idx ) ).emp_id;

getConnector().getStatement().executeUpdate( sql );

String statStr = first_name + " " + last_name + " has been marked ";

if ( newInOut.equals( "Y" ) )
statStr += "in";
else
statStr += "out";

showStatus( statStr );
}
catch ( SQLException e )
{
errorLog( "Error during save: " + e.toString() );
showStatus( "Error during save: " + e.toString() );
return( false );
}

// Replace the visual...
theList.replaceItem( si, idx );

// Reselect...
theList.select( idx );

// I handled it!
return( true );
}

The toggleListItem() method is unique because it rebuilds the current string and replaces it in the list. After the user selects an employee, this method strips the displayed line down to its components. It removes the "In" or "Out" from the end of the line and appends the toggled equivalent. The result is stored in the database. Only after a successful save to the database is the string replaced in the list, showing the new status.

A Refresh Timer

The last user interface piece that you need to create is the refresh timer. This little guy is responsible for going out and refreshing the list of employees. The refresh timer is necessary because your target users might have this running at all times. Other employees will utilize this and its underlying database. If it is never refreshed, it would show only the employee's status that the user toggled and the initial values loaded. Using a refresh, however, you can keep your data up-to-date! First, create an EventTimer to be your refresh timer. If you remember back in , "Utility Classes," the EventTimer is ideal for adding to programs where the underlying interface cannot or should not be changed. This is a case of that. You don't want to change the in-terface of your derived class. It could end up causing other problems down the road, not to mention other people may be using this class. You don't want to change things on them. The use of this EventTimer is perfect.

Creating your timer is a matter of using the new operator:
// Get the refresh rate from the configuration file...
int refreshRate =
Integer.parseInt( getParameter( "refresh.rate", "60000" ) );

// Create a timer to refresh...
myTimer = new EventTimer( this, refreshRate );

You also query your configuration properties list for a refresh.rate property. If it isn't there, you default to a one minute default refresh rate. Otherwise the refresh rate is used.

Caution
Remember, the refresh.rate configuration parameter should hold values that specify the number of milliseconds between time-outs. Be careful what you place in there.

Finally, when a timer event does occur, you refresh your list:

// Did my timer fire?
if ( myTimer == arg )
{
showStatus( "Refreshing..." );

// Reload the panel...
loadPanel();

showStatus( "Refreshed!" );

return( true );
}

Programming Considerations

Aside from the database access, this app is one of the simplest in your intranet app suite. It does nothing more than toggle a single column from Y to N, then back again. This app presented you with very little challenge. You needed to present an intuitive interface to the user, while making it quick and easy to use. You did this by using the List class. To recap, this app introduced the following Java intranet coding topics:

Summary

This chapter introduced you to the fourth sample app in your intranet app suite: the Online In/Out Board. This program is only responsible for updating a single column in the employee table. This program will be useful for any employee who needs to know the whereabouts of another employee. Likely uses will be for receptionists who take messages for individuals. In , "Online Employee Phonebook," you will design and create an Employee Phonebook app. This program allows employees to look at the phone numbers of their coworkers.


Java ScreenshotJava ScreenshotJava ScreenshotJava Screenshot



Comments