Tải bản đầy đủ (.pdf) (44 trang)

My SQL and Java Developer’s Guide phần 7 ppsx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (238.82 KB, 44 trang )

public int ejbCreate(String username, String password)
throws CreateException, RemoteException {
}
public void ejbPostCreate(String course, String instructor) {
}
public int ejbLoad() throws RemoteException {
}
public int ejbStore()throws RemoteException {
}
public int ejbRemove()throws RemoteException {
}
public String findByPrimaryKey() throws RemoteException {
}
}
ejbCreate()
We can use the ejbCreate() method when we want to insert a new row into the
database. To accomplish this, we need to access the database, check to see if
the row already exists, and either insert a row into the table or throw an excep-
tion. The code might look like this:
public int ejbCreate(String username, String password)
throws CreateException, RemoveException {
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)
ctx.lookup( "java:comp/env/jdbc/AccountsDB" );
Connection conn = ds.getConnection();
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM acc_acc WHERE username=? and password=?" );
stmt.setString( 1, username );
stmt.setString(2, password);
ResultSet rs = stmt.executeQuery();
duplicateKey = rs.next();


rs.close();
stmt.close();
if ( ! duplicateKey ) {
stmt = conn.prepareStatement(
"INSERT INTO acc_acc (null, ?, ?, ? ,?)");
stmt.setString( 1, username );
stmt.setString( 2, password );
stmt.setTimestamp(null);
stmt.setTimestamp(new Timestamp(new
Date().getDate()));
Bean-Managed Persistence
241
stmt.executeUpdate();
stmt.close();
}
conn.close();
} catch (Exception ex) {
throw new java.rmi.RemoteException( "ejbCreate Error", ex );
}
if ( duplicateKey )
throw new javax.ejb.DuplicateKeyException();
return null;
}
As the preceding code shows, we are responsible for doing all of the database
work in a bean-managed persistence mode.
ejbLoad()
In the ejbLoad() method, the code has to load all of the fields for the row asso-
ciated with a particular primary key. For example:
public void ejbLoad()
throws java.rmi.RemoteException

{
boolean found = false;
try {
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)
ctx.lookup( "java:comp/env/jdbc/AccountsDB" );
Connection conn = ds.getConnection();
PreparedStatement stmt = conn.prepareStatement(
"SELECT username FROM acc_acc WHERE acc_id=?");
stmt.setString( 1, acc_id );
ResultSet rs = stmt.executeQuery();
if ( rs.next()) {
found = true;
username = rs.getString(2);
password = rs.getString(3);
ts = rs.getTimestamp(4);
act_ts = re.getTimestamp(5);
}
rs.close();
stmt.close();
conn.close();
} catch (Exception ex) {
throw new java.rmi.RemoteException( "ejbLoad Error", ex );
}
if ( ! found )
throw new java.rmi.RemoteException( "Bean not found" );
}
EJBs with MySQL
242
In this code, the database table is checked for a row with a specific acc_id

value. If a match is found, all of the bean’s attributes are set based on the
returned row; otherwise, an exception is thrown.
ejbStore()
The ejbStore() method is responsible for placing the bean’s information back
into the database table. This isn’t an INSERT into the table, but an UPDATE of
a previously created row. The code might look like this:
public void ejbStore()
throws java.rmi.RemoteException
{
boolean found = false;
try {
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)
ctx.lookup( "java:comp/env/jdbc/AccountsDB" );
Connection conn = ds.getConnection();
PreparedStatement stmt = conn.prepareStatement(
"UPDATE acc_acc set username=?, password=?,
ts=? act_ts =?WHERE acc_id=?");
stmt.setString( 1, username );
stmt.setString( 2, password );
stmt.setTimestamp( 3, ts );
stmt.setTimestamp 4, act_ts );
stmt.setInt( 5, acc_id );
if (stmt.executeUpdate() <= 0) {
throw new java.rmi.RemoteException( "Bean not found" );
}
stmt.close();
conn.close();
} catch (Exception ex) {
throw new java.rmi.RemoteException( "ejbLoad Error", ex );

}
}
This code performs an UPDATE on the database table using the values stored
in the attributes of the bean.
ejbRemove()
The ejbRemove() method is designed to delete the rows from the database for
the table based on the current acc_id of the bean. The code looks like this:
public void ejbRemove()
throws java.rmi.RemoteException
{
Bean-Managed Persistence
243
boolean found = false;
try {
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)
ctx.lookup( "java:comp/env/jdbc/AccountsDB" );
Connection conn = ds.getConnection();
PreparedStatement stmt = conn.prepareStatement(
"DELETE FROM acc_acc WHERE acc_id=?");
stmt.setInt( 1, acc_id );
stmt.close();
conn.close();
} catch (Exception ex) {
throw new java.rmi.RemoteException( "ejbLoad Error", ex );
}
}
ejbFindByPrimaryKey()
The ejbFindByPrimaryKey() method returns a String representation of the pri-
mary key for the current bean. The code checks to make sure the row exists in

the table before returning the account id. The code for the method is
public String findByPrimaryKey() throws RemoteException {
try {
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)
ctx.lookup( "java:comp/env/jdbc/AccountsDB" );
Connection conn = ds.getConnection();
PreparedStatement stmt = conn.prepareStatement(
"SELECT username FROM acc_acc WHERE acc_id=?");
stmt.setString( 1, acc_id );
ResultSet rs = stmt.executeQuery();
if ( rs.next()) {
rs.close();
stmt.close();
conn.close();
return ""+acc_id;
} else {
throw new java.rmi.FinderException(
"findByPrimaryKey Error", ex );
}
} catch (Exception ex) {
throw new java.rmi.RemoteException( "ejbLoad Error", ex );
}
}
EJBs with MySQL
244
Setter/Getter Methods
You need to implement all of the setter/getter methods defined in the CMP bean
in the BMP bean, but you have to set/get the attributes of the bean yourself.
Once all of the methods have been placed in the bean, there will be no notice-

able difference between the CMP and the BMP beans.
What’s Next
In this chapter, we presented a simple example of how to build an entity bean
that will access data in a MySQL database using the Connector/J driver. In the
next chapter, we build a MySQL database interface.
What’s Next
245

I
n previous chapters, we have explored many of the elements involved in
bringing MySQL and Java together to address database-related needs. In
this chapter, we shift our focus to building a general database application.
For the sake of maximizing both generality and potential usefulness, we will
build a graphical interface for MySQL. Such an application offers ample oppor-
tunity to demonstrate a number of Java’s strengths, both in terms of the lan-
guage itself and its ability to work with data sources via the JDBC API.
The application we present in this chapter is built around the notion of data-
base tasks. Over the course of development, we introduce four such tasks. The
first provides information about the underlying JDBC driver and database prod-
uct, including names and version numbers. The second provides an interface
for executing arbitrary SQL queries. The third provides the user with informa-
tion regarding the column definitions for a given table. Finally, we define a task
that allows the user to insert a new row into an existing table. Along the way,
we also develop a number of utility classes that support extensibility by
simplifying input, output, task delegation, exception handling, and session ini-
tiation. Given our focus, the user interface is of course less refined than it might
be for a production application. Furthermore, depending on user needs, the
individual tasks might lack a degree of functionality (e.g., meaningful handling
of binary data); however, the application should convey some of what is
possible and provide a foundation for developing and implementing more

sophisticated tasks.
Building a General Interface
for MySQL
CHAPTER
12
247
Tasks
With the exception of the most generic of interfaces, the range of operations
you might need to carry out on a data source is so diverse that attempting to
capture all of them in a single application is impractical. While generic inter-
faces certainly have their place, they tend to require more knowledge and expe-
rience on the part of the user. By moving away from a generic interface, you
find it becomes easier to tailor operations to specific user tasks.
It is the notion of specific user tasks that drives the design of our MySQL inter-
face. However, if the interface is to remain generally useful, it is important that
the task code not be tied too tightly to framework of the interface. Unless the
addition and removal of tasks is straightforward, an interface that is useful for
one user might very well be useless for another. An obvious approach to
addressing this issue involves viewing tasks as removable modules and building
a framework in support of such modules. While it is not within the scope of this
chapter to build a complete, full-featured framework for such modules, the
example application does provide one possible approach to developing such a
framework.
Accepting that our interface is to be built around task modules, the obvious
question becomes one of what constitutes a task. From the user’s perspective,
a task is some useful unit of work; whether that corresponds to retrieving a triv-
ial piece of information from the database or carrying out a complex transac-
tion depends on the needs of a particular user. From the perspective of our
interface design, a task is an entity that has a name, a delegate class, and a flag
indicating whether it is currently enabled. The name is a simple task identifier

used by interface components that must deal with task identity. The delegate
class represents the type of the object to which task completion is delegated.
The enabled flag provides a technique for specifying whether the task is to be
considered active by the interface. This task definition is captured and encap-
sulated by the TaskDefinition class shown in Listing 12.1.
Building a General Interface for MySQL
248
package mysqljava;
public class TaskDefinition
{
public TaskDefinition( String name,
Class delegate, boolean enabled )
{
Listing 12.1 The class representing task modules. (continues)
Tasks
249
this.name = name;
this.delegate = delegate;
this.enabled = enabled;
}
public String getName()
{
return (name);
}
public Class getDelegate()
{
return (delegate);
}
public boolean isEnabled()
{

return (enabled);
}
private String name;
private Class delegate;
private boolean enabled;
}
Listing 12.1 The class representing task modules. (continued)
Holding to the goal of simple task addition and removal, defined tasks are pro-
vided through a configuration file containing entries like the following:
mysqljava.DbInfo:enabled:Database Info
mysqljava.ShowColumns:disabled:Show Columns
mysqljava.SqlQuery:disabled:SQL Query
mysqljava.InsertRow:disabled:Insert Row
Each entry consists of three colon-delimited fields. The first field specifies the
fully qualified name of the delegate class. The second field is the flag indicating
whether the task should be considered active. The final field is the name asso-
ciated with the task. These are simply textual representations of the fields
encapsulated by our TaskDefinition class. How these fields are used to support
pluggable modules should become clear in later sections. For now, take a look
at Listing 12.2 to see how the configuration file is processed to generate a task
list. Given an InputStreamReader representing a configuration file, an object of
type Tasks parses the task definition entries, converts the fields to appropriate
types, creates corresponding TaskDefinition objects, and makes the task list
available via an Enumeration.
Building a General Interface for MySQL
250
package mysqljava;
import java.io.*;
import java.util.*;
public class Tasks

{
public Tasks( InputStreamReader taskS )
{
readTasks( taskS );
}
public int getTaskCount()
{
return (taskDefs.size());
}
public Enumeration getTasks()
{
return (taskDefs.elements());
}
private void readTasks( InputStreamReader taskS )
{
try
{
BufferedReader reader = new BufferedReader( taskS );
String taskLine;
while ( (taskLine = reader.readLine()) != null )
{
addTaskDefinition( taskLine );
}
}
catch( IOException ioX )
{
System.err.println( "Failed to fully parse task file: "
+ ioX );
}
}

private void addTaskDefinition( String taskLine )
{
StringTokenizer taskTok = new StringTokenizer( taskLine,
Listing 12.2 The class representing the list of defined tasks modules. (continues)
Tasks
251
DELIM );
if ( taskTok.countTokens() != TOKEN_NUM )
{
System.err.println( "Invalid task definition: "
+ taskLine );
return;
}
Class taskClass = null;
String taskClassName = taskTok.nextToken();
try
{
taskClass = Class.forName( taskClassName );
}
catch( ClassNotFoundException cnfX )
{
System.err.println( "Class '" + taskClassName
+ "' not found: " + cnfX );
return;
}
boolean taskEnabled = false;
if ( taskTok.nextToken().equalsIgnoreCase( "enabled" ) )
{
taskEnabled = true;
}

String taskName = taskTok.nextToken();
TaskDefinition def = new TaskDefinition( taskName,
taskClass,
taskEnabled );
taskDefs.add( def );
}
private Vector taskDefs = new Vector();
final static int TOKEN_NUM = 3;
final static String DELIM = ":";
}
Listing 12.2 The class representing the list of defined tasks modules. (continued)
SQL Exceptions
One common feature shared by most of the JDBC API methods is that they
make use of the SQLException class, either directly or indirectly, through
derived exception classes. As such, it is worth a little upfront effort to provide
some generalized SQLException processing. For the purposes of our sample
application, we limit ourselves to a single message-processing method. This
method, defined in our SqlExceptionReader class, is shown in Listing 12.3.
In addition to the functionality inherited from the java.lang.Exception, SQLEx-
ception provides a SQLState code, a vendor-specific exception code, and the
ability to chain additional SQLException objects. The readException() method
of SqlExceptionReader extracts the additional fields, stepping through the
exception chain if necessary, and builds an exception message containing the
available information.
Building a General Interface for MySQL
252
package mysqljava;
import java.sql.*;
public class SqlExceptionReader
{

public static String readException( SQLException sqlX )
{
StringBuffer msg = new StringBuffer( 1024 );
SQLException nextX;
int exceptionNumber = 0;
do
{
++exceptionNumber;
msg.append( "Exception " + exceptionNumber + ": \n" );
msg.append( " Message: " + sqlX.getMessage() + "\n" );
msg.append( " State : " + sqlX.getSQLState() + "\n" );
msg.append( " Code : " + sqlX.getErrorCode() + "\n" );
}
while ( (nextX = sqlX.getNextException()) != null );
return (msg.toString());
}
}
Listing 12.3 A class for reading SQLExceptions.
MySQL Connections
Since a data source connection is a prerequisite for any task involving commu-
nication with a database, it makes sense to capture the required connection
data in a common class. We do this with the ConnectionData class shown in
Listing 12.4. This class represents a host name and port, a database name, and
a username and password. Accessors for username and password are provided,
along with an accessor that returns a MySQL-compatible URL of the form
jdbc:mysql://hostname:port/database_name
Perhaps more useful is the class’s buildConnection() method, which uses the
contained URL data, username, and password to obtain and return a Connec-
tion object. As is seen in the listing, we opted to use the DriverManager
approach to obtaining a Connection object. Depending on your environment, it

might make more sense to obtain Connection objects from a source imple-
menting the DataSource or ConnectionPoolDataSource interfaces specified in
the javax.sql package.
MySQL Connections
253
package mysqljava;
import java.sql.*;
public class ConnectionData
{
public ConnectionData( String hostName,
String dbName,
String port,
String username,
String password )
{
this.hostName = hostName;
this.dbName = dbName;
this.port = port;
this.username = username;
this.password = password;
}
public String getUsername()
{
return (username);
}
public String getPassword()
Listing 12.4 The class used for establishing database connections. (continues)
Building a General Interface for MySQL
254
{

return (password);
}
public String getUrl()
{
String url = "jdbc:mysql://" + hostName
+ ":" + port + "/" + dbName;
return (url);
}
public Connection buildConnection()
{
try
{
Class.forName( "com.mysql.jdbc.Driver" );
}
catch( ClassNotFoundException cnfX )
{
cnfX.printStackTrace( System.err );
return (null);
}
try
{
Connection conn = DriverManager.getConnection(
getUrl(),
getUsername(),
getPassword() );
return (conn);
}
catch( SQLException sqlX )
{
System.out.println(

SqlExceptionReader.readException( sqlX ) );
return (null);
}
}
private String hostName;
private String dbName;
private String port;
private String username;
private String password;
}
Listing 12.4 The class used for establishing database connections. (continued)
The Task Delegate
In defining the interface’s view of a task, we introduced the notion of a task
delegate. As implied by the name, this is an entity to which the interface dele-
gates responsibility for task execution, whatever that might involve. The dele-
gate might in turn hand over responsibility for portions of the task to other
entities; however, a major design goal is that our interface need not be con-
cerned with what happens after it has dispatched the task. In working toward
this goal, we introduce the TaskDelegate Java interface shown in Listing 12.5.
This is a Java language interface that must be implemented by any class that is
to serve as a task delegate. When a delegate’s execute() method is invoked, the
caller is responsible for providing an appropriate session object. The method’s
return value indicates only whether the task is successfully dispatched; this
does not necessarily correspond to successful task execution.
While TaskDelegate is trivial in appearance, it is an important piece of our
design. In addition to explicitly stating the method(s) that delegates must
support, it allows the interface to treat all task delegate objects as instances of
type TaskDelegate, regardless of the underlying object type. This allows our
interface to rely on polymorphism for proper dispatch and simplifies the
process of obtaining delegate instances via Java’s reflection facilities. The net

result is that our interface is capable of dispatching tasks in a straightforward
manner without any knowledge of the task’s implementation, aside from the
name of its delegate class.
The Task Manager
255
package mysqljava;
import java.sql.*;
public interface TaskDelegate
{
public boolean execute( Connection conn );
}
Listing 12.5 The task delegate interface.
The Task Manager
Access to defined tasks begins with the task manager. This is a graphical inter-
face that supports task selection and input of database connection parameters.
Figure 12.1 shows a task manager with four defined tasks. Our task manager
consists of two primary pieces. The first, and more interesting of the two, is the
TaskPanel class shown in Listing 12.6. The second is the TaskManager class
shown in Listing 12.7.
The TaskPanel class, with the help of its three inner classes, provides the bulk
of the task management interface. The inner classes include ConnectionPane,
TaskPane, and TaskHandler, which are responsible for connection parameter
input, task selection, and task dispatch, respectively. The ConnectionPane sim-
ply provides for input of the information required by our ConnectionData class,
namely a host name and port, a database name, and a username and password.
The TaskPane provides a set of buttons for task selection. The number of but-
tons, and the manner in which they are named, is based on the task list that is
loaded when the application is launched; in other words, the buttons corre-
spond to the enabled tasks specified in the configuration file. The TaskHandler
class is an ActionListener responsible for handling ActionEvents associated

with the buttons on the TaskPane.
When a user clicks on a task button, the TaskHandler requests the Connec-
tionData object associated with the ConnectionPane and attempts to build a
connection with the specified database. If it obtains a valid Connection
object, it then requests the task list and iterates through the list looking for a
TaskDefinition object with a name that matches that provided by the task but-
ton. If a matching TaskDefinition is located, reflection is used to obtain an
instance of the corresponding delegate class. The delegate object is then cast
to a TaskDelegate since that is known to be a least common denominator for
all delegate classes. Finally, the TaskDelegate execute() method is used to
dispatch the task.
The second piece of our task manager, the TaskManager class, is the applica-
tion driver. It parses the configuration file, builds the task list, and provides the
main application frame and menu bar. By default, it expects to find a configu-
ration file named tasks.conf; however, an alternate configuration file may be
provided via the command line.
Building a General Interface for MySQL
256
Figure 12.1 The task manager.
The Task Manager
257
package mysqljava;
import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
public class TaskPanel extends JPanel
{

public TaskPanel( Tasks taskList )
{
this.taskList = taskList;
setLayout( new BorderLayout() );
connPane = new ConnectionPane();
connPane.setBorder( new TitledBorder( "Connection Data" ) );
taskPane = new TaskPane();
taskPane.setBorder( new TitledBorder( "Tasks" ) );
add( connPane, BorderLayout.NORTH );
add( taskPane, BorderLayout.SOUTH );
}
private Tasks taskList;
private ConnectionPane connPane;
private TaskPane taskPane;
class ConnectionPane extends JPanel
{
ConnectionPane()
{
setLayout( new GridLayout( 5, 2 ) );
add( hostNameLabel );
add( hostNameField );
add( dbNameLabel );
add( dbNameField );
add( portNumberLabel );
add( portNumberField );
add( usernameLabel );
add( usernameField );
add( passwordLabel );
Listing 12.6 The task manager's TaskPanel component. (continues)
Building a General Interface for MySQL

258
add( passwordField );
}
ConnectionData getConnectionData()
{
String password =
new String( passwordField.getPassword() );
ConnectionData data = new ConnectionData(
hostNameField.getText(),
dbNameField.getText(),
portNumberField.getText(),
usernameField.getText(),
password );
return (data);
}
private JLabel hostNameLabel = new JLabel( "Host Name:" );
private JLabel dbNameLabel= new JLabel( "Database Name:" );
private JLabel portNumberLabel =
new JLabel( "Port Number:" );
private JLabel usernameLabel = new JLabel( "Username:" );
private JLabel passwordLabel = new JLabel( "Password:" );
private JTextField hostNameField = new JTextField( 20 );
private JTextField dbNameField = new JTextField( 20 );
private JTextField portNumberField =
new JTextField( "3306", 6 );
private JTextField usernameField = new JTextField( 20 );
private JPasswordField passwordField =
new JPasswordField( 20 );
}
class TaskPane extends JPanel

{
TaskPane()
{
int taskCount = TaskPanel.this.taskList.getTaskCount();
int rows = ((taskCount % COLS) == 0)
? (taskCount / COLS)
: ((taskCount / COLS) + 1);
setLayout( new GridLayout( rows, COLS ) );
Listing 12.6 The task manager's TaskPanel component. (continues)
The Task Manager
259
taskButtons = new JButton[taskCount];
TaskHandler handler = new TaskHandler();
Enumeration tasks = taskList.getTasks();
int task = 0;
while ( tasks.hasMoreElements() )
{
TaskDefinition taskDef =
(TaskDefinition)(tasks.nextElement());
if ( ! taskDef.isEnabled() )
{
continue;
}
String taskName = taskDef.getName();
taskButtons[task] = new JButton( taskName );
taskButtons[task].addActionListener( handler );
add( taskButtons[task++] );
}
}
private JButton[] taskButtons;

final static int COLS = 2;
}
class TaskHandler implements ActionListener
{
public void actionPerformed( ActionEvent ae )
{
ConnectionData connData = connPane.getConnectionData();
Connection conn = connData.buildConnection();
if ( conn == null )
{
String msg = "Could not build connection. Check\n"
+ "provided connection data and verify\n"
+ "server availability.";
JOptionPane.showMessageDialog(
TaskPanel.this, msg,
"Connection Failure",
Listing 12.6 The task manager's TaskPanel component. (continues)
Building a General Interface for MySQL
260
JOptionPane.ERROR_MESSAGE );
return;
}
String taskName = ae.getActionCommand();
Enumeration tasks = taskList.getTasks();
boolean dispatched = false;
while ( tasks.hasMoreElements() )
{
TaskDefinition taskDef =
(TaskDefinition)(tasks.nextElement());
if ( ! taskDef.isEnabled() )

{
continue;
}
if ( taskName.equals( taskDef.getName() ) )
{
try
{
Class delegateClass = taskDef.getDelegate();
Object delegateObject = delegateClass.newInstance();
TaskDelegate delegate = (TaskDelegate)delegateObject;
dispatched = delegate.execute( conn );
if ( ! dispatched )
{
String msg = "Could not execute task: "
+ taskDef.getName();
JOptionPane.showMessageDialog(
TaskPanel.this, msg,
"Task Failure",
JOptionPane.ERROR_MESSAGE );
}
}
catch( InstantiationException iX )
{
String msg = "Failed to instantiate "
+ "delegate for task: "
+ taskDef.getName();
Listing 12.6 The task manager's TaskPanel component. (continues)
The Task Manager
261
JOptionPane.showMessageDialog(

TaskPanel.this, msg,
"Task Failure",
JOptionPane.ERROR_MESSAGE );
}
catch( IllegalAccessException iaX )
{
String msg = "Cound not access delegate for task: "
+ taskDef.getName();
JOptionPane.showMessageDialog(
TaskPanel.this, msg,
"Task Failure",
JOptionPane.ERROR_MESSAGE );
}
break;
}
}
if ( ! dispatched )
{
try
{
conn.close();
}
catch( SQLException sqlX ) {}
}
}
}
}
Listing 12.6 The task manager's TaskPanel component. (continued)
package mysqljava;
import java.io.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.sql.*;
Listing 12.7 The task manager's TaskManager component. (continues)
Building a General Interface for MySQL
262
public class TaskManager extends JFrame
{
TaskManager( Tasks taskList )
{
super( "MySQL-Java Task Manager" );
this.taskList = taskList;
buildGui();
pack();
setVisible( true );
}
private void buildGui()
{
fileMenu.add( fileExit );
menuBar.add( fileMenu );
setJMenuBar( menuBar );
frameContainer.setLayout( new BorderLayout() );
frameContainer.add( new TaskPanel( taskList ) );
setContentPane( frameContainer );
addWindowListener( new WindowHandler() );
fileExit.addActionListener( new MenuHandler() );
}
private JPanel frameContainer = new JPanel();
private JMenuBar menuBar = new JMenuBar();

private JMenu fileMenu = new JMenu( "File" );
private JMenuItem fileExit = new JMenuItem( "Exit" );
private Tasks taskList;
class WindowHandler extends WindowAdapter
{
public void windowClosing( WindowEvent we )
{
System.exit( 0 );
}
}
class MenuHandler implements ActionListener
{
Listing 12.7 The task manager's TaskManager component. (continues)
The Task Manager
263
public void actionPerformed( ActionEvent ae )
{
if ( ae.getActionCommand().equals( "Exit" ) )
{
System.exit( 0 );
}
}
}
public static void main( String[] args )
{
String configFileName = "tasks.conf";
if ( args.length == 1 )
{
configFileName = args[0];
}

File configFile = new File( configFileName );
if ( ! configFile.exists() || ! configFile.canRead() )
{
System.err.println( "Can't read config file '"
+ configFileName + "'" );
System.exit( 1 );
}
FileReader configReader = null;
try
{
configReader = new FileReader( configFile );
}
catch( FileNotFoundException fnfX ) {}
Tasks taskList = new Tasks( configReader );
try
{
configReader.close();
}
catch( IOException ioX ) {}
TaskManager ex = new TaskManager( taskList );
}
}
Listing 12.7 The task manager's TaskManager component. (continued)
Task Results
Since an application concerned with database-related tasks is almost certain to
generate at least some results that are best displayed in table format, we now
turn to addressing that need. Through its Swing package, Java provides elegant
support for rendering tables, and we take advantage of that fact here. We start
by extending Java’s AbstractTableModel class, as shown in Listing 12.8. Our
derived class, ResultsTableModel, assumes a Vector representation of table

data. The constructor expects that each element of a supplied Vector is an array
of Strings, with the first containing the column names and each subsequent ele-
ment representing one row of data. Responsibility for ensuring appropriate
String representations for each data element rests with the entity instantiating
the ResultsTableModel object.
Building a General Interface for MySQL
264
package mysqljava;
import java.util.*;
import javax.swing.table.*;
public class ResultsTableModel extends AbstractTableModel
{
ResultsTableModel( Vector results )
{
columnNames = (String[])(results.get( 0 ));
results.remove( 0 );
int rowCount = results.size();
tableData = new String [rowCount][];
for ( int i = 0; i < rowCount; ++i )
{
tableData[i] = (String[])(results.get( i ));
}
}
public String getColumnName( int colIndex )
{
return (columnNames[colIndex]);
}
public int getColumnCount()
{
return (columnNames.length);

}
Listing 12.8 The model used for displaying results in table format. (continues)
Task Results
265
public int getRowCount()
{
return (tableData.length);
}
public Object getValueAt( int rowIndex, int colIndex )
{
return (tableData[rowIndex][colIndex]);
}
private String[] columnNames;
private String[][] tableData;
}
Listing 12.8 The model used for displaying results in table format. (continued)
With a model in place for our table data, we next turn to displaying that data.
Responsibility for constructing and populating the table rests with our Results-
TablePanel class, which is shown in Listing 12.9. Although somewhat limited in
functionality, it gets the job done. Columns are sized based on the anticipated
display length of their respective column names, and the resulting table is
placed in a scroll pane. A great deal more can be done with Java tables; how-
ever, plumbing the depths of table support is beyond the scope of this book.
package mysqljava;
import java.util.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class ResultsTablePanel extends JPanel
{

public ResultsTablePanel( Vector results )
{
ResultsTableModel model = new ResultsTableModel( results );
JTable resultsTable = new JTable( model );
resultsTable.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
setColumnWidths( resultsTable );
Listing 12.9 The results table panel. (continues)

×