BUY THIS BOOK

Safari Books Online

What is this?

Looking to Reprint this content?


Java Enterprise in a Nutshell
Java Enterprise in a Nutshell, Second Edition
Pages: 992

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: Introduction
This book is an introduction to, and quick reference for, the Java Enterprise APIs. Some of these APIs are a core part of the Java platform, while others are standard extensions to the platform. Together, however, they enable Java programs to use and interact with a suite of distributed network services that are commonly used in enterprise computing.
These APIs can be used individually to integrate specific enterprise functionality into your applications. Or, you can use them within the Java 2 Platform, Enterprise Edition (J2EE), which integrates all of the APIs discussed in this book into a well-defined application framework. What's the difference? Well, besides guaranteeing a certain level of support for the various enterprise-related Java APIs, a compliant J2EE server also provides certain application services that are critical for developing, deploying, and managing applications in an enterprise environment. These include application assembly and deployment facilities that let you configure runtime application properties and resources at deploy time, in a standard format, as well as a unified security model that applies to various types of components that can be defined within J2EE.
Before we go any further, let's be clear. The term enterprise computing is simply a synonym for distributed computing: computation done by groups of programs interacting over a network.
Anyone can write distributed applications: you don't have to work for a major corporation, university, government agency, or any other kind of large-scale "enterprise" to program with the Java Enterprise APIs. Small businesses may not have the same enterprise-scale distributed computing needs large organizations have, but most still engage in plenty of distributed computing. With the explosive growth of the Internet and of network services, just about anyone can find a reason to write distributed applications. One such reason is that it is fun. When distributed computing is used to leverage the power of the network, the results can be amazingly cool!
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Enterprise Computing Defined
Before we go any further, let's be clear. The term enterprise computing is simply a synonym for distributed computing: computation done by groups of programs interacting over a network.
Anyone can write distributed applications: you don't have to work for a major corporation, university, government agency, or any other kind of large-scale "enterprise" to program with the Java Enterprise APIs. Small businesses may not have the same enterprise-scale distributed computing needs large organizations have, but most still engage in plenty of distributed computing. With the explosive growth of the Internet and of network services, just about anyone can find a reason to write distributed applications. One such reason is that it is fun. When distributed computing is used to leverage the power of the network, the results can be amazingly cool!
So, if the Java Enterprise APIs aren't used exclusively by enterprises, why aren't they called the Java Distributed Computing APIs? The reasons are simple. First, enterprise is a hot buzzword these days -- everyone in the networking industry wants to be working on enterprise something. Second, large enterprises have lots of money to spend on costly hardware for running their expensive network server software. Since the enterprise is where the money is, we get the word "enterprise" in the APIs.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Enterprise Computing Demystified
Enterprise computing has a reputation for complexity and, for the uninitiated, is often surrounded by a shroud of mystery. Here are some reasons enterprise computing can seem intimidating:
  • Enterprise computing usually takes place in a heterogeneous network: one in which the computers range from large mainframes and supercomputers down to PCs (including both top-of-the-line 64-bit processors and outdated 386's). The computers were purchased at different times from a variety of different vendors and run two or three or more different operating systems. The only common denominator is that all the computers in the network speak the same fundamental network protocol (usually TCP/IP).
  • A variety of server applications run on top of the heterogeneous network hardware. An enterprise might have database software from three different companies, each of which defines different, incompatible extensions.
  • Enterprise computing involves the use of many different network protocols and standards. Some standards overlap in small or significant ways. Many have been extended in various vendor-specific, nonstandard ways. Some are quite old and use a vocabulary and terminology that dates back to an earlier era of computing. This creates a confusing alphabet soup of acronyms.
  • Enterprise computing has only recently emerged as an integrated discipline of its own. Although today enterprise development models are becoming more cohesive and encompassing, many enterprises are still left with lots of "legacy systems" that are aggregated in an ad hoc way.
  • Enterprise programmers, like many of us in the high-tech world, tend to make their work seem more complicated than it actually is. This is a natural human tendency -- to be part of the "in" group and keep outsiders out -- but this seems somehow magnified within the computer industry.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The Java Enterprise APIs
The Java Enterprise APIs provide support for a number of the most commonly used distributed computing technologies and network services. These APIs are described in the sections that follow. The APIs are building blocks for distributed applications. At the end of the chapter, there are descriptions of some enterprise computing scenarios that illustrate how these separate APIs can be used together to produce an enterprise application.
JDBC (Java Database Connectivity) is the Java Enterprise API for working with relational database systems. JDBC allows a Java program to send SQL query and update statements to a database server and to retrieve and iterate through query results returned by the server. JDBC also allows you to get metainformation about the database and its tables from the database server.
The JDBC API is independent of vendor-specific APIs defined by particular database systems. The JDBC architecture relies upon a Driver class that hides the details of communicating with a database server. Each database server product requires a custom Driver implementation to allow Java programs to communicate with it. Major database vendors have made JDBC drivers available for their products. In addition, a "bridge" driver exists to enable Java programs to communicate with databases through existing ODBC drivers.
The JDBC API is found in the java.sql package, which was introduced in Java 1.1. Java 2 (including the 1.2, 1.3, and 1.4 versions) updated the core APIs to use JDBC 2.0, which adds a number of new classes to this package to support advanced database features. JDBC 2.0 also provides additional features in the javax.sql standard extension package. javax.sql includes classes for treating database query results as JavaBeans, for pooling database connections, and for obtaining database connection information from a name service. The extension package also supports scrollable result sets, batch updates, and the storage of Java objects in databases. AS of this printing, the JDBC 3.0 specifications have been finalized and should be available in mid-2002.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Enterprise Computing Scenarios
The previous sections have been rapid-fire introductions to the Java Enterprise APIs that are part of the J2EE framework. Don't worry if you didn't understand all the information presented there: the rest of the chapters in this Part cover the APIs in more detail. The important message you should take from this chapter is that the Java Enterprise APIs are building blocks that work together to enable you to write distributed Java applications for enterprise computing. The network infrastructure of every enterprise is unique, and the Java Enterprise APIs can be combined in any number of ways to meet the specific needs and goals of a particular enterprise.
shows a network schematic for a hypothetical enterprise. It illustrates some of the many possible interconnections among network services and shows the Java Enterprise APIs that facilitate those interconnections. The figure is followed by example scenarios that demonstrate how the Java Enterprise APIs might be used to solve typical enterprise computing problems. You may find it useful to refer to while reading through the scenarios, but note that the figure doesn't illustrate the specific scenarios presented here.
Figure 1-1: The distributed computing architecture of a hypothetical enterprise
CornCo Inc. runs a successful catalog-based mail-order business selling fresh flavored popcorn. They want to expand into the exciting world of electronic commerce over the Internet. Here's how they might do it:
  • A customer visits the company's web site, www.cornco.com, and uses a web browser to interact with the company's web server. This allows the customer to view the company's products and make selections to purchase.
  • The web server uses a shopping-cart servlet to keep track of the products the customer has chosen to buy. The HTTP protocol is itself stateless, but servlets can persist between client requests, so this shopping-cart servlet can remember the customer's selections even while the customer continues to browse the web site.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Other Enterprise APIs
There are a number of initiatives and APIs brewing in the Java community that could be classified as "enterprise APIs" but have not been included in this Nutshell book. We mention a few of them here, for the interested reader.
The area of XML-based "web services" (services that can be discovered and invoked using XML-based protocols delivered over HTTP) is a hotbed of activity at the time of this writing. The combination of XML, a portable data/content framework, and HTTP, a ubiquitous communications protocol, is a natural and powerful one. APIs and frameworks are being proposed and developed around a number of XML-based protocols such as SOAP, WSDL, UDDI, ebXML, etc. Sun has initiated a series of its own APIs around XML (http://java.sun.com/xml). We discuss JAXP in some detail in the book (see ). There are others not covered in this book, such as the Java API for XML Messaging (JAXM), the Java API for XML Registries (JAXR), which supports ebXML. and the Java API for XML Binding (JAXB), which provides a means for marshalling and unmarshalling Java Objects to and from XML representations.
There are also a number of other third-party efforts to support web services in Java, being driven by IBM, BEA, and other players in the Java community, as well as the open source efforts of Apache (http://www.apache.org), OASIS (http://www.oasis-open.org) and others. At the time of this writing, the subject of XML-based web services is somewhat unfocused in the market, and more complete coverage requires much more material than we could provide in this Nutshell book. However, the Java XML APIs will receive more coverage in their own volume once they mature.
Jini (http://www.sun.com/jini) is a next-generation networking system designed to enable instantaneous networking between unrelated devices, without external communication. Jini is a system for distributed computing; it includes a name service, a distributed transaction service, and a distributed event service. Although these services overlap with JNDI, JTS, and JMS, Jini is fundamentally different from these J2EE APIs. The Enterprise APIs are designed to bring Java into existing enterprises and to interoperate with existing protocols and services. Jini, on the other hand, is a next-generation networking system that was designed from scratch, with no concern for compatibility with today's distributed systems.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: JDBC
The JDBC API provides Java applications with mid-level access to most database systems, via the Structured Query Language (SQL). JDBC is a key enterprise API, as it's hard to imagine an enterprise application that doesn't use a database in some way.
In the first edition of this book, we focused on the original JDBC 1.0 API, and touched briefly on the new features provided by the JDBC 2.0 API. JDBC 2.1 is now a standard component of the J2SE platform, and drivers supporting the upgraded specification are widely available. In this edition, we discuss the JDBC 2.1 API and the JDBC 2.0 Optional Packages (previously known as the JDBC 2.0 Standard Extension) and take a look at the upcoming JDBC 3.0 API.
A word of caution: while the java.sql package is not tremendously complex, it does require grounding in general database concepts and the SQL language itself. This book includes a brief SQL reference (see , but if you have never worked with a relational database system before, this chapter is not the place to start. For a more complete treatment of JDBC and general database concepts, we recommend Database Programming with JDBC and Java by George Reese (O'Reilly).
Different database systems have surprisingly little in common: just a similar purpose and a mostly compatible query language. Beyond that, every database has its own API that you must learn to write programs that interact with the database. This has meant that writing code capable of interfacing with databases from more than one vendor has been a daunting challenge. Cross-database APIs exist, most notably Microsoft's ODBC API, but these tend to find themselves, at best, limited to a particular platform.
JDBC is Sun's attempt to create a platform-neutral interface between databases and Java. With JDBC, you can count on a standard set of database access features and (usually) a particular subset of SQL, SQL-92. The JDBC API defines a set of interfaces that encapsulate major database functionality, including running queries, processing results, and determining configuration information. A database vendor or third-party developer writes a JDBC
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
JDBC Architecture
Different database systems have surprisingly little in common: just a similar purpose and a mostly compatible query language. Beyond that, every database has its own API that you must learn to write programs that interact with the database. This has meant that writing code capable of interfacing with databases from more than one vendor has been a daunting challenge. Cross-database APIs exist, most notably Microsoft's ODBC API, but these tend to find themselves, at best, limited to a particular platform.
JDBC is Sun's attempt to create a platform-neutral interface between databases and Java. With JDBC, you can count on a standard set of database access features and (usually) a particular subset of SQL, SQL-92. The JDBC API defines a set of interfaces that encapsulate major database functionality, including running queries, processing results, and determining configuration information. A database vendor or third-party developer writes a JDBC driver, which is a set of classes that implements these interfaces for a particular database system. An application can use a number of drivers interchangeably. shows how an application uses JDBC to interact with one or more databases without knowing about the underlying driver implementations.
Figure 2-1: JDBC-database interaction
Before we discuss all of the individual components of JDBC, let's look at a simple example that incorporates most of the major pieces of JDBC functionality. loads a driver, connects to the database, executes some SQL, and retrieves the results. It also keeps an eye out for any database-related errors.
Example 2-1. A Simple JDBC Example
import java.sql.*;

public class JDBCSample {

 public static void main(java.lang.String[] args) {
   try {
     // This is where we load the driver
     Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
   } 
   catch (ClassNotFoundException e) {
     System.out.println("Unable to load Driver Class");
     return;
   }
 
   try {
     // All database access is within a try/catch block. Connect to database,
     // specifying particular database, username, and password
     Connection con = DriverManager.getConnection("jdbc:odbc:companydb",
              "", "");
 
     // Create and execute an SQL Statement
     Statement stmt = con.createStatement(  );
     ResultSet rs = stmt.executeQuery("SELECT FIRST_NAME FROM EMPLOYEES");

     // Display the SQL Results
     while(rs.next(  )) {
       System.out.println(rs.getString("FIRST_NAME"));
     }

     // Make sure our database resources are released
     rs.close(  );
     stmt.close(  );
     con.close(  );

     } 
     catch (SQLException se) {
       // Inform user of any SQL errors
       System.out.println("SQL Exception: " + se.getMessage(  ));
       se.printStackTrace(System.out);
      } 
    } 
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Connecting to the Database
The java.sql. Connection object, which encapsulates a single connection to a particular database, forms the basis of all JDBC data-handling code. An application can maintain multiple connections, up to the limits imposed by the database system itself. A standard small office or web server Oracle installation can support 50 or so connections, while a major corporate database could host several thousand. The DriverManager.getConnection( ) method creates a connection:
Connection con = DriverManager.getConnection("url", "user", "password");
You pass three arguments to getConnection( ): a JDBC URL, a database username, and a password. For databases that don't require explicit logins, the user and password strings should be left blank. When the method is called, the DriverManager queries each registered driver, asking if it understands the URL. If a driver recognizes the URL, it returns a Connection object. Because the getConnection( ) method checks each driver in turn, you should avoid loading more drivers than are necessary for your application.
The getConnection( ) method has two other variants that are less frequently used. One variant takes a single String argument and tries to create a connection to that JDBC URL without a username or password, or with a username and password embedded in the URL itself. The other version takes a JDBC URL and a java.util.Properties object that contains a set of name/value pairs. You generally need to provide at least username= value and password= value pairs.
When a Connection has outlived its usefulness, you should be sure to explicitly close it by calling its close( ) method. This frees up any memory being used by the object, and, more importantly, it releases any other database resources the connection may be holding on to. These resources (cursors, handles, and so on) can be much more valuable than a few bytes of memory, as they are often quite limited. This is particularly important in applications such as servlets that might need to create and destroy thousands of JDBC connections between restarts. Because of the way some JDBC drivers are designed, it is not safe to rely on Java's garbage collection to remove unneeded JDBC connections.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Statements
Once you have created a Connection, you can begin using it to execute SQL statements. This is usually done via Statement objects. There are actually three kinds of statements in JDBC:
Statement
Represents a basic SQL statement
PreparedStatement
Represents a precompiled SQL statement, which can offer improved performance
CallableStatement
Allows JDBC programs complete access to stored procedures within the database itself
We're just going to discuss the Statement object for now; PreparedStatement and CallableStatement are covered in detail later in this chapter.
To get a Statement object, call the createStatement( ) method of a Connection:
Statement stmt = con.createStatement(  );
Once you have created a Statement, use it to execute SQL statements. A statement can either be a query that returns results or an operation that manipulates the database in some way. If you are performing a query, use the executeQuery( ) method of the Statement object:
ResultSet rs = stmt.executeQuery("SELECT * FROM CUSTOMERS");
Here we've used executeQuery( ) to run a SELECT statement. This call returns a ResultSet object that contains the results of the query (we'll take a closer look at ResultSet in the next section).
Statement also provides an executeUpdate( ) method, for running SQL statements that don't return results, such as the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Results
When an SQL query executes, the results form a pseudo-table that contains all rows that fit the query criteria. For instance, here's a textual representation of the results of the query string "SELECT NAME, CUSTOMER_ID, PHONE FROM CUSTOMERS":
NAME                             CUSTOMER_ID  PHONE
-------------------------------- ----------- -------------------
Jane Markham                      1           617 555-1212
Louis Smith                       2           617 555-1213
Woodrow Lang                      3           508 555-7171
Dr. John Smith                    4           (011) 42 323-1239
This kind of textual representation is not very useful for Java programs. Instead, JDBC uses the java.sql.ResultSet interface to encapsulate the query results as Java primitive types and objects. You can think of a ResultSet as an object that represents an underlying table of query results, where you use method calls to navigate between rows and retrieve particular column values.
A Java program might handle the previous query as follows:
Statement stmt = con.createStatement(  );
ResultSet rs = stmt.executeQuery(
 "SELECT NAME, CUSTOMER_ID, PHONE FROM CUSTOMERS");

while(rs.next(  )) {
 System.out.print("Customer #" + rs.getString("CUSTOMER_ID"));
 System.out.print(", " + rs.getString("NAME"));
 System.out.println(", is at " + rs.getString("PHONE");
}
rs.close(  );
stmt.close(  );
Here's the resulting output:
Customer #1, Jane Markham, is at 617 555-1212
Customer #2, Louis Smith, is at 617 555-1213
Customer #3, Woodrow Lang, is at 508 555-7171
Customer #4, Dr. John Smith, is at (011) 42 323-1239
The code loops through each row of the ResultSet using the next( ) method. When you start working with a ResultSet, you are positioned before the first row of results. That means you have to call next( ) once just to access the first row. Each time you call
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Handling Errors
Any JDBC object that encounters an error serious enough to halt execution throws a SQLException. For example, database connection errors, malformed SQL statements, and insufficient database privileges all throw SQLException objects.
The SQLException class extends the normal java.lang.Exception class and defines an additional method called getNextException( ). This allows JDBC classes to chain a series of SQLException objects together. SQLException also defines the getSQLState( ) and getErrorCode( ) methods to provide additional information about an error. The value returned by getSQLState( ) is one of the ANSI-92 SQL state codes; these codes are listed in . getErrorCode( ) returns a vendor-specific error code.
An extremely conscientious application might have a catch block that looks something like this:
try {
 // Actual database code
} 
catch (SQLException e) {
 while(e != null) { 
 System.out.println("\nSQL Exception:");
 System.out.println(e.getMessage(  ));
 System.out.println("ANSI-92 SQL State: " + e.getSQLState(  ));
 System.out.println("Vendor Error Code: " + e.getErrorCode(  ));
 e = e.getNextException(  );
 } 
}
JDBC classes also have the option of generating (but not throwing) a SQLWarning exception when something is not quite right, but at the same time, not sufficiently serious to warrant halting the entire program. For example, attempting to set a transaction isolation mode that is not supported by the underlying database might generate a warning rather than an exception. Remember, exactly what qualifies as a warning condition varies by database.
SQLWarning encapsulates the same information as SQLException and is used in a similar fashion. However, unlike SQLException
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Prepared Statements
The PreparedStatement object is a close relative of the Statement object. Both accomplish roughly the same thing: running SQL statements. PreparedStatement, however, allows you to precompile your SQL and run it repeatedly, adjusting specific parameters as necessary. Since processing SQL strings is a large part of a database's overhead, getting compilation out of the way at the start can significantly improve performance. With proper use, it can also simplify otherwise tedious database tasks.
As with Statement, you create a PreparedStatement object from a Connection object. In this case, though, the SQL is specified at creation instead of execution, using the prepareStatement( ) method of Connection:
PreparedStatement pstmt = con.prepareStatement(
 "INSERT INTO EMPLOYEES (NAME, PHONE) VALUES (?, ?)");
This SQL statement inserts a new row into the EMPLOYEES table, setting the NAME and PHONE columns to certain values. Since the whole point of a PreparedStatement is to be able to execute the statement repeatedly, we don't specify values in the call to prepareStatement( ), but instead use question marks (?) to indicate parameters for the statement. To actually run the statement, we specify values for the parameters and then execute the statement:
pstmt.clearParameters(  );
pstmt.setString(1, "Jimmy Adelphi");
pstmt.setString(2, "201 555-7823");
pstmt.executeUpdate(  );
Before setting parameters, we clear out any previously specified parameters with the clearParameters( ) method. Then we can set the value for each parameter (indexed from 1 to the number of question marks) using the setString( ) method. PreparedStatement defines numerous setXXX( ) methods for specifying different types of parameters; see the java.sql reference material later in this book for a complete list. Finally, we use the executeUpdate( )
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
BLOBs and CLOBs
As users began to increase the volume of data stored in databases, vendors introduced support for Large Objects (LOBs). The two varieties of LOBs, binary large objects (BLOBs) and character large objects (CLOBs), store large amounts of binary or character data, respectively.
Support for LOB types across databases varies. Some don't support them at all, and most have unique type names (BINARY, LONG RAW, and so forth). JDBC 1.0 makes programs retrieve BLOB and CLOB data using the getBinaryStream( ) or getAsciiStream( ) methods. (A third method, getUnicodeStream( ), has been deprecated in favor of the new getCharacterStream( ) method, which returns a Reader.)
In JDBC 2.0, the ResultSet interface includes getBlob( ) and getClob( ) methods, which return Blob and Clob objects, respectively. The Blob and Clob objects themselves allow access to their data via streams (the getBinaryStream( ) method of Blob and the getCharacterStream( ) method of Clob) or via direct-read methods (the getBytes( ) method of Blob and the getSubString( ) method of Clob).
To retrieve the data from a CLOB, simply retrieve the Clob object and call the getCharacterStream( ) method:
String s;
Clob clob = blobResultSet.getBlob("CLOBFIELD");
BufferedReader clobData = new BufferedReader(clob.getCharacterStream(  ));
while((s = clobData.readLine(  )) != null)
  System.out.println(s);
In addition, you can set Blob and Clob objects when you are working with a PreparedStatement, using the setBlob( ) and setClob( ) methods. While the API provides update methods for streams, there are no updateBlob( ) or updateClob( ) methods, and the Blob interface provides no mechanism for altering the contents of a Blob already stored in the database (although some drivers support updating of BLOB and CLOB types via the setBinaryStream( ) and
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Metadata
Most JDBC programs are designed to work with a specific database and particular tables in that database; the program knows exactly what kind of data it is dealing with. Some applications, however, need to dynamically discover information about result set structures or underlying database configurations. This information is called metadata, and JDBC provides two classes for dealing with it: DatabaseMetaData and ResultSetMetaData. If you are developing a JDBC application that will be deployed outside a known environment, you need to be familiar with these interfaces.
You can retrieve general information about the structure of a database with the java.sql.DatabaseMetaData interface. By making thorough use of this class, a program can tailor its SQL and use of JDBC on the fly, to accommodate different levels of database and JDBC driver support.
Database metadata is associated with a particular connection, so DatabaseMetaData objects are created with the getMetaData( ) method of Connection:
DatabaseMetaData dbmeta = con.getMetaData(  );
DatabaseMetaData provides an overwhelming number of methods you can call to get actual configuration information about the database. Some of these return String objects (getURL( )), some return boolean values (nullsAreSortedHigh( )), and still others return integers (getMaxConnections( )).
A number of other methods return ResultSet objects. These methods, such as getColumns( ), getTableTypes( ), and getPrivileges( ), generally encapsulate complex or variable-length information. The getTables( ) method, for instance, returns a ResultSet that contains the name of every table in the database as well as a good deal of extra information.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Transactions
A transaction is a group of several operations that must behave atomically, i.e., as if they are a single, indivisible operation. With regards to databases, transactions allow you to combine one or more database actions into a single atomic unit. If you have an application that needs to execute multiple SQL statements to fulfill one goal (say, an inventory management system that needs to move items from an INVENTORY table to a SHIPPING table), you probably want to use JDBC's transaction services to accomplish the goal.
Working with a transaction involves the following steps: start the transaction, perform its component operations, and then either commit the transaction if all the component operations succeed or roll it back if one of the operations fails. The ability to roll back a transaction is the key feature. This means that if any one SQL statement fails, the entire operation fails, and it is as though none of the component operations took place. Therefore it is impossible to end up with a situation where, for example, the INVENTORY table has been debited, but the SHIPPING table has not been credited.
Another issue with transactions and databases concerns changes to the database becoming visible to the rest of the system. Transactions can operate at varying levels of isolation from the rest of the database. At the most isolated level, the results of all the component SQL statements become visible to the rest of the system only when the transaction is committed. In other words, nobody sees the reduced inventory before the shipping data is updated.
The Connection object in JDBC is responsible for transaction management. With JDBC, you are always using transactions in some form. By default, a new connection starts out in transaction auto-commit mode, which means that every SQL statement is executed as an individual transaction that is immediately committed to the database.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Stored Procedures
Most RDBMS systems include some sort of internal programming language (e.g., Oracle's PL/SQL). These languages allow database developers to embed procedural application code directly within the database and then call that code from other applications. The advantage of this approach is that the code can be written just once and then used in multiple different applications (even with different platforms and languages). It also allows application code to be divorced from the underlying table structure. If stored procedures handle all of the SQL, and applications just call the procedures, only the stored procedures need to be modified if the table structure is changed later on.
Here is an Oracle PL/SQL stored procedure:
CREATE OR REPLACE PROCEDURE sp_interest
(id IN INTEGER
bal IN OUT FLOAT) IS
BEGIN
SELECT balance
INTO bal
FROM accounts
WHERE account_id = id;

bal := bal + bal * 0.03;

UPDATE accounts
SET balance = bal
WHERE account_id = id;

END;
This PL/SQL procedure takes two input values, an account ID and a balance, and returns an updated balance.
The CallableStatement interface is the JDBC object that supports stored procedures. The Connection class has a prepareCall( ) method that is very similar to the prepareStatement( ) method we used to create a PreparedStatement. Because each database has its own syntax for accessing stored procedures, JDBC defines a standardized escape syntax for accessing stored procedures with CallableStatement. The syntax for a stored procedure that doesn't return a result set is:
{call procedure_name[(?[,?...])]}
The syntax for a stored procedure that returns a result is:
{? = call procedure_name[(?[,?...])]}
In this syntax, each question mark (?) represents a placeholder for a procedure parameter or a return value. Note that the parameters are optional. The JDBC driver is responsible for translating the escape syntax into the database's own stored procedure syntax.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Escape Sequences
Escape sequences allow JDBC programs to package certain database commands in a database-independent manner. Since different databases implement different features (especially scalar SQL functions) in different ways, in order to be truly portable, JDBC needs to provide a standard way to access at least a subset of that functionality. We've already seen escape sequences twice: with the various SQL date and time functions, and with the CallableStatement object.
A JDBC escape sequence consists of a pair of curly braces, a keyword, and a set of parameters. Thus, call is the keyword for stored procedures, while d, t, and ts are keywords for dates and times. One keyword we haven't seen yet is escape. This keyword specifies the character that is used to escape wildcard characters in a LIKE statement:
stmt.executeQuery(
 "SELECT * FROM ApiDocs WHERE Field_Name like 'TRANS\_%' {escape '\'}");
Normally, the underscore ( _ ) character is treated as a single-character wildcard, while the percent sign (%) is the multiple-character wildcard. By specifying the backslash (\) as the escape character, we can match on the underscore character itself. Note that the escape keyword can also be used outside wildcard searches. For example, SQL string termination characters (such as the single quote) need to be escaped when appearing within strings.
The fn keyword allows the use of internal scalar database functions. Scalar functions are a fairly standard component of most database architectures, even though the actual implementations vary. For instance, many databases support the SOUNDEX(string) function, which translates a character string into a numerical representation of its sound. Another function, DIFFERENCE(string1, string2), computes the difference between the soundex values for two strings. If the values are close enough, you can assume the two words sound the same ("Beacon" and "Bacon"). If your database supports
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The JDBC Optional Package
The javax.sql package is an optional extension of the JDBC 2.1 API. It includes support for a variety of enterprise-development activities. It's a standard component of the J2EE platform, and the supporting classes can also be downloaded separately for use with any Java 2 system.
The DataSource interface provides an alternative to the DriverManager class and conventional JDBC URLs. Instead, information about a database is stored within a naming service and retrieved via the JNDI API. Connection information (drivers, server locations, and so forth) are stored within the DataSource object, which uses them to create the actual Connection object used to execute JDBC commands. DataSource objects are also used to provide native driver-level support for connection pooling and distributed transactions.
Each DataSource is assigned a logical name, by convention beginning with "jdbc/". The logical name and associated connection metadata are configured in the J2EE setup process. This makes code more portable and allows for easy changes in drivers and connection information. Accessing a DataSource via JNDI is very simple:
Context ctx = new InitialContext(  ); 
DataSource ds = (DataSource)ctx.lookup("jdbc/CamelDB"); 
Connection con = ds.getConnection("lawrence", "arabia");
The first two lines obtain the DataSource object from the naming service. The getConnection( ) method of DataSource then logs into the database and returns a Connection object. Unlike DriverManager, the only information required is a username and password.

Section 2.12.1.1: Connection pooling

The ConnectionPoolDataSource provides a transparent interface to a "pool" of available Connection
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
JDBC 3.0
At the time of this writing, Version 3.0 of the JDBC API was in its fourth proposed final draft, and was on target for inclusion in J2SE and J2EE Version 1.4. JDBC 3.0 adds increased support for SQL99 features, increasingly capable transaction support, full read/write handling of BLOB and CLOB fields, URL datatypes, and various minor enhancements to the rest of the API. JDBC 3.0 adds a number of methods to the DatabaseMetaData interface, allowing programmers to determine which new features are supported.
JDBC 3.0 is also intended to integrate well with the J2EE Connector standard, allowing drivers and configuration information to be packaged into a Resource adapter ARchive, or RAR file. This allows easier deployment of JDBC connections into a J2EE server, but doesn't change the way programmers interact with the API.
Savepoints allow transactions to be partially rolled back. If the underlying database and driver support the functionality, the new setSavepoint(String name) method of Connection creates a named savepoint in the current transaction, and returns an object implementing the Savepoint interface. The object can be passed to the rollback( ) method of Connection to roll back all components of the current transaction that took place after the setSavepoint( ) method was called:
Statement stmt = con.createStatement(  );
stmt.executeUpdate("delete from clients");
stmt.executeUpdate("insert into clients (NAME, ID) values ('Charles Babbage', 1)");
Savepoint save = con.setSavepoint("INSERT_POINT");
stmt.executeUpdate("update clients set NAME = 'Ada Lovelace' where ID = 1");
con.rollback(save);
con.commit(  );
This example will leave the "clients" table with a single row, with a value of 1 in the ID column and "Charles Babbage" in the NAME column.
Savepoints can't be used in distributed transactions.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 3: Remote Method Invocation
This chapter examines the Java Remote Method Invocation (RMI) API -- Java's native scheme for creating and using remote objects. Java RMI provides the following elements:
  • Remote object implementations
  • Client interfaces, or stubs, to remote objects
  • A remote object registry for finding objects on the network
  • A facility for automatically creating (activating) remote objects on-demand
  • A network protocol for communication between remote objects and their client
Each of these elements (except the last one) has a Java interface defined for it within the java.rmi package and its subpackages, which comprise the RMI API. Using these interfaces, you can develop remote objects and the clients that use them to create a distributed application that resides on hosts across the network.
RMI is the distributed object system that is built into the core Java environment. You can think of RMI as a built-in facility for Java that allows you to interact with objects that are actually running in Java virtual machines on remote hosts on the network. With RMI (and other distributed object APIs we discuss in this book), you can get a reference to an object that "lives" in a remote process and invokes methods on it as if it were a local object running within the same virtual machine as your code (hence the name, "Remote Method Invocation API").
Another way to characterize RMI (and other remote object schemes) is in terms of the granularity of the distribution that it enables. The Java servlet and JavaServer Page (JSP) APIs, described in Chapters 5 and 6, allow you to distribute applications at the view level. Putting a servlet or JSP front-end on your server-side object model serves to export a web-based (typically HTML) interface to your application, which any remote web browser can access. The Java networking APIs, embodied in the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Introduction to RMI
RMI is the distributed object system that is built into the core Java environment. You can think of RMI as a built-in facility for Java that allows you to interact with objects that are actually running in Java virtual machines on remote hosts on the network. With RMI (and other distributed object APIs we discuss in this book), you can get a reference to an object that "lives" in a remote process and invokes methods on it as if it were a local object running within the same virtual machine as your code (hence the name, "Remote Method Invocation API").
Another way to characterize RMI (and other remote object schemes) is in terms of the granularity of the distribution that it enables. The Java servlet and JavaServer Page (JSP) APIs, described in Chapters 5 and 6, allow you to distribute applications at the view level. Putting a servlet or JSP front-end on your server-side object model serves to export a web-based (typically HTML) interface to your application, which any remote web browser can access. The Java networking APIs, embodied in the java.net and java.io packages, allow you to open up very narrow, low-level data connections to your Java process, for simple data exchanges or "heartbeat" purposes (make a successful connection and transmit a few bytes to confirm that a process is alive). RMI and other remote object systems fall somewhere in-between the two. They allow you to export functionality at the object level, allowing remote clients to interact directly with individual objects, in the same way they do with local objects: using (remote) method calls.
RMI was added to the core Java API in Version 1.1 of the JDK (and enhanced for Version 1.2 of the Java 2 platform), in recognition of the critical need for support for distributed objects in distributed-application development. Prior to RMI and other remote object schemes, writing a distributed application involved basic socket programming, in which a "raw" communication channel was used to pass messages and data between two remote processes. Now, with RMI and distributed objects, you can "export" an object as a remote object, so that other remote processes/agents can access it directly as a Java object. So, instead of defining a low-level message protocol and data transmission format between processes in your distributed application, use Java interfaces as the "protocol" and the exported method arguments become the data transmission format. The distributed object system (RMI in this case) handles all the underlying networking needed to make your remote method calls work.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Defining Remote Objects
Now that you have a basic idea of how Java RMI works, we can explore the details of creating and using distributed objects with RMI in more detail. As mentioned earlier, defining a remote RMI object involves specifying a remote interface for the object, then providing a class that implements this interface. The remote interface and implementation class are then used by RMI to generate a client stub and server skeleton for your remote object. The communication between local objects and remote objects is handled using these client stubs and server skeletons. The relationships among stubs, skeletons, and the objects that use them are shown in .
Figure 3-2: Relationships among remote object, stub, and skeleton classes
When a client gets a reference to a remote object (details on how this reference is obtained come later) and then calls methods on this object reference, there needs to be a way for the method request to get transmitted back to the actual object on the remote server and for the results of the method call to get transmitted back to the client. This is what the generated stub and skeleton classes are for. They act as the communication link between the client and your exported remote object, making it seem to the client that the object actually exists within its Java VM.
The RMI compiler (rmic) automatically generates these stub and skeleton classes for you. Based on the remote interface and implementation class you provide, rmic generates stub and skeleton classes that implement the remote interface and act as go-betweens for the client application and the actual server object. For the client stub class, the compiler generates an implementation of each remote method that simply packages up (marshals) the method arguments and transmits them to the server. For the server skeleton class, the RMI compiler generates another set of implementations of the remote methods, but these are designed to receive the method arguments from the remote method call, unpackage them, and make the corresponding method call on the object implementation. Whatever the method call generates (return data or an exception), the results are packaged and transmitted back to the remote client. The client stub method (which is still executing at this point) unpackages the results and delivers them to the client as the result of its remote method call.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Creating the Stubs and Skeletons
After you define the remote Java interface and implementation class, compile them into Java bytecodes using a standard Java compiler. Then you use the RMI stub/skeleton compiler, rmic , to generate the stub and skeleton interfaces that are used at either end of the RMI communication link, as was shown in . In its simplest form, you can run rmic with the fully qualified classname of your implementation class as the only argument. For example, once we've compiled the Account and AccountImpl classes, we can generate the stubs and skeletons for the remote Account object with the following command (Unix version):
% rmic AccountImpl
If the RMI compiler is successful, this command generates the stub and skeleton classes, AccountImpl_Stub and AccountImpl_Skel, in the current directory. The rmic compiler has additional arguments that let you specify where the generated classes should be stored, whether to print warnings, etc. For example, if you want the stub and skeleton classes to reside in the directory /usr/local/classes, you can run the command using the -d option:
% rmic -d /usr/local/classes AccountImpl
This command generates the stub and skeleton classes in the specified directory. A full description of the rmic utility and its options is given in .
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Accessing Remote Objects as a Client
Now that we've defined a remote object interface and its server implementation, and generated the stub and skeleton classes that RMI uses to establish the link between the server object and the remote client, it's time to look at how you make your remote objects available to remote clients.
The first remote object reference in an RMI distributed application is typically obtained through the RMI registry facility and the Naming interface. Every host that wants to export remote references to local Java objects must be running an RMI registry daemon of some kind. A registry daemon listens (on a particular port) for requests from remote clients for references to objects served on that host. The standard Sun Java SDK distribution provides an RMI registry daemon, rmiregistry. This utility simply creates a Registry object that listens to a specified port and then goes into a wait loop, waiting for local processes to register objects with it or for clients to connect and look up RMI objects in its registry. You start the registry daemon by running the rmiregistry command, with an optional argument that specifies a port to listen to:
objhost% rmiregistry 5000 &
Without the port argument, the RMI registry daemon listens on port 1099. Typically, you run the registry daemon in the background (i.e., put an & at the end of the command on a Unix system or run start rmiregistry [ port ] in a DOS window on a Windows system), or run it as a service at startup.
Once the RMI registry is running on a host, you can register remote objects with it using one of these classes: the java.rmi.registry.Registry interface, the java.rmi.registry.LocateRegistry class, or the java.rmi.Naming class.
A
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Dynamically Loaded Classes
The RMI runtime system has a dynamic class-loading facility that loads the classes it needs while executing remote method calls. In some situations, you don't need to worry much about how your application classes are obtained by the various agents in an RMI application. This is especially true if you have direct access to all hosts involved in the distributed system (i.e., if you can install your application classes in the local CLASSPATH for each machine participating in the application). For instance, when discussing the earlier Account example, we assumed all the relevant classes (Account, AccountImpl, stub, and skeleton classes) were installed on both the client and the server. However, if your distributed application involves remote agents running on hosts that aren't directly under your control, you need to understand how RMI loads classes at runtime, so you can ensure that each remote agent can find the classes it needs in order to run.
As with any Java application, the Java runtime system is responsible for loading the classes needed to initiate an RMI session. Starting an interaction with a remote object means loading the RMI API classes themselves, as well as the base interface for the remote object and the stub class for the remote interface. On the server side, the skeleton class for the remote object and the actual implementati