Newsletter sign-up
View all newsletters

Sign up for our technology specific newsletters.

Enterprise Java
Email Address:

Start saddling up for Mustang

Explore some of the new features in Java SE 6

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone

Page 2 of 5

For example, you can call public String readLine() to return a line of characters without prompting the user. This method is demonstrated by the following code fragment:

 String input = console.readLine ();
Note
The Console object is associated with a unique java.io.Reader object and a unique java.io.PrintWriter object. Invoke Console's public Reader reader() method to return the Reader. You can then pass the Reader to one of java.util.Scanner's constructors to perform sophisticated parsing of the console input stream, for example. To obtain the PrintWriter, invoke Console's public PrintWriter writer() method. You can then invoke a variety of useful methods for outputting different typed data to the console. For convenience, Console provides a public void flush() method that invokes PrinterWriter's flush() method.

There is something interesting about both readPassword() methods and both readLine() methods. When these methods encounter a problem with I/O, they do not throw a java.io.IOException object (which is thrown by System.in.read(), for example). Instead, each method throws a java.io.IOError object. Because IOError subclasses Error, you do not need to catch this object, unlike IOException.

The counterpart methods for outputting characters to the console output stream are public Console format(String fmt, Object... args) and the equivalent public Console printf(String fmt, Object... args) convenience method, which internally invokes format(). The code fragment below demonstrates printf():

console.printf ("%s", input);

After outputting characters, format() -- and, by extension, printf() -- automatically flushes the console output stream.

Let's put our knowledge of console I/O to some practical use—database access. To that end, I've created a Microsoft Access sales.mdb sales database, which is distributed with this article's code (downloadable from Resources). This database contains a single territories table with two columns: Name, for a salesperson's name, and Territory, the locations where the salesperson can legally sell products. I've populated territories with the data below, and I've password-protected the database -- mustang is the password.

| ------------------------ |
| Name        | Territory  |
| ------------------------ |
| John Doe    | North      | 
| Jane Doe    | South      | 
| Paul Smith  | East       |
| Kathy Smith | West       |
| ------------------------ |

The sales database will be accessed from a Sales application. Prior to accessing the database, the application requests a username and password. It then uses these values in an attempt to connect, via the JDBC-ODBC bridge driver, to the sales datasource (which identifies the location of sales.mdb and which you create with the Windows Control Panel ODBC Data Sources applet). If the connection succeeds, the application outputs the values in all rows and columns of the territories table. Listing 1 presents the source code to the Sales application.

Listing 1. Sales.java

// Sales.java

import java.io.*; import java.sql.*; import java.util.*;

class Sales { public static void main (String [] args) throws ClassNotFoundException, SQLException { // Attempt to obtain a console.

Console console = System.console (); if (console == null)

{ System.err.println ("sales: unable to obtain console"); return; }

// Obtain username.

String username = console.readLine ("Enter username: ");

// Obtain password.

String password = new String (console.readPassword ("Enter password: "));

// Create a Vector datastructure for holding table records.

Vector<Object []> v = new Vector<Object []> ();

Connection con = null; Statement stmt = null; ResultSet rs;

try { // Attempt to connect to the sales datasource.

con = DriverManager.getConnection ("jdbc:odbc:sales", username, password);

// Garbage collect the password—not a good idea to keep passwords // hanging around.

password = null;

// Attempt to create a statement.

stmt = con.createStatement ();

// Establish the maximum number of rows that can be returned.

stmt.setMaxRows (10);

// Attempt to fetch all rows from the territories table.

String query = "SELECT * FROM territories"; rs = stmt.executeQuery (query);

// Get number of columns.

int nCols = rs.getMetaData ().getColumnCount ();

// Read all rows of all columns into storage.

int i = 0;

while (rs.next ()) { Object [] buffer = new Object [nCols];

for (int j = 0; j < nCols; j++) buffer [j] = rs.getObject (j + 1);

// NOTE: getObject requires a 1-based column index.

v.add (buffer); }

// Extract rows from the array.

Object [] rows = v.toArray (); for (i = 0; i < rows.length; i++) { // Extract columns from the row.

Object [] cols = (Object []) rows [i];

// Print out the values from each column.

for (int j = 0; j < cols.length; j++) console.printf ("%s ", cols [j]);

console.printf ("\n"); }

console.printf ("Value at Row 1, Col 0 = %s\n", getValue (v, 1, 0)); } catch (SQLException e) { console.printf ("sales: %s\n", e.getMessage ()); } finally { if (stmt != null) try { stmt.close (); } catch (SQLException e2) {}

if (con != null) try { con.close (); } catch (SQLException e2) {} } }

static Object getValue (Vector v, int row, int col) { // The following conversion should really not be done in this method, // because it's inefficient. Placing the conversion here is a matter of // convenience.

Object [] rows = v.toArray ();

Object [] cols = (Object []) rows [row];

return cols [col]; } }

Compile Sales.java and run the application. You will be greeted with an Enter username: prompt. Type anything you like for the username, but you must enter something. You will next be prompted to type the password. Make sure to specify mustang. Assuming a connection is successfully established, you should see the table values I presented earlier.

Note
Look closely at Listing 1. The code does not try to load the JDBC-ODBC bridge driver via Class.forName(). Because Mustang includes JDBC 4.0, whose DriverManager class provides a transparent automatic driver-loading mechanism to locate and load driver classes, you no longer need to explicitly load driver classes with Class.forName().

Listing 1 uses Console's printf() method to output the table contents. However, this approach has a subtle problem. If the territories table had many rows of data, not all of these rows would fit within the console window without scrolling. Normally, to capture these rows, you would redirect standard output to a file. But if you do that, System.console() returns null, and you cannot use console I/O. Hence, think carefully before using Console's printf() and format() methods in your own applications. Don't use them to output large quantities of data to the screen; otherwise you will not be able to view all of that data.

Although Console addresses the original RFE, many developers take issue with this class. Their criticisms include:

  • The System.console() method should have been named System.getConsole(). However, some disagree, arguing that the "get" prefix only applies to beans and System is not a bean. Furthermore, console() is a static method, which means IDEs don't consider this method to be a property getter.
  • The Console class should have provided the capability to read single characters without buffering. In other words, a program should not have to wait for the user to press the Enter key before reading user input. A method similar to C's kbhit() function would help address this criticism by returning a Boolean indicating whether any keys have been pressed. If a true value returns, another method would be called to return the next keystroke waiting in the BIOS keystroke buffer.
  • Console should have provided the capability to enable/disable the echoing of characters to the console. A pair of methods similar to C's getch() (get character without echoing that character to the console and block only if no characters are waiting in the BIOS keystroke buffer) and getche() (get character and echo that character to the console and block only if no characters are waiting in the BIOS keystroke buffer) functions would address this criticism.
  • Console should have provided a screen-clearing capability and other features found in the Unix-based curses library.
  • Console should have provided a detach() method to detach an application from the console and send it into the background.

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comment
Login
Forgot your account info?
Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources