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
The JDBC API is found in the
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 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.
The JDBC API is simple and well-designed. Programmers who are familiar with SQL and database programming in general should find it very easy to work with databases in Java. See Chapter 2 for details on JDBC and Chapter 12 for a quick reference to SQL.
invocation is a programming model that provides a
high-level, generic approach to
distributed computing. This model
extends the object-oriented programming paradigm to distributed
client-server programming: it allows a client to communicate with a
server by invoking methods on remote objects that reside on the
server. RMI is implemented in the
and its subpackages, which were introduced in Java 1.1 and were
enhanced in Versions 1.2, 1.3, and 1.4 of the Java 2 platform.
The Java RMI implementation is
full-featured, but still simple and easy to use. It gains much of its
simplicity by being built on top of a network-centric and dynamically
extensible platform, of course. But it also gains simplicity by
requiring both client and server to be implemented in Java. This
requirement ensures that both client and server share a common set of
data types and have access to the
serialization and deserialization features of the
java.io package, for example. On the other hand,
this means that it is more difficult to use RMI with distributed
objects written in languages other than Java, such as objects that
exist on legacy servers. The default remote-method communication
protocol used by RMI will only allow Java code to interact with RMI
objects. So one option for interfacing with non-Java legacy code over
RMI is to use the
Interface (JNI). Another is to use RMI/IIOP, which was made a
standard part of the core Java APIs in Version 1.3 of the Java 2
platform. RMI/IIOP is an optional communication protocol that allows
RMI objects to interact with
remote objects. Since CORBA objects can be implemented in many
languages, this also bridges the language gap. We discuss both of
these options in this book, but in practice, RMI is an excellent
distributed object solution for situations in which it is clear that
clients and servers will be written in Java. Fortunately, there are
many such situations.
it easy to create networked,
Programmers who have spent time writing networked applications using
lower-level protocols are usually amazed by the power of RMI. By
making RMI so easy,
java.rmi points the way to
future applications and systems that consist of loose groups of
objects interacting with each other over a network. These objects may
act both as clients, by calling methods of other objects, and as
servers, by exposing their own methods to other objects. See Chapter 3 for a tutorial on using RMI.
As we’ve just discussed, RMI is a distributed object solution that works especially well when both client and server are written in Java. It is more work, and therefore less attractive, in heterogeneous environments in which clients and servers may be written in arbitrary languages. For environments like these, the Java 2 platform includes a CORBA-based solution for remote method invocation on distributed objects.
(Common Object Request Broker Architecture) is a widely used standard
defined by the Object Management Group (OMG). The Java binding of
this standard is implemented as a core part of the Java 2 platform in
org.omg.CORBA package and its subpackages. The
implementation includes a simple
Object Request Broker
(ORB) that a Java application can use to communicate (as both a
client and a server) with other ORBs, and thus with other CORBA
The interfaces to remote CORBA objects are described in a platform- and language-independent way with the Interface Definition Language (IDL). Sun provides an IDL compiler that translates an IDL declaration of a remote interface into the Java stub classes needed for implementing the IDL interface in Java, or for connecting to a remote implementation of the interface from your Java code.
A number of Java implementations of the CORBA standard are available from various vendors. This book documents Sun’s implementation, known as Java IDL. It is covered in detail in Chapter 4. The syntax of the IDL language itself is summarized in Chapter 14.
The eXtensible Markup Language (XML) is a nearly ubiquitous presence in enterprise development. Data and content storage was just the start for XML; it’s rapidly spread to become a powerful tool in messaging, RPC, web interfaces, enterprise system integration, and other areas. J2EE 1.3-compliant application servers include the Java API for XML Parsing (JAXP), which is a pluggable API that supports both SAX and DOM parsing of XML content, as well as XSLT-based transformations of XML. JAXP 1.1, the version noted in the J2EE 1.3 specification, supports the SAX 2 and DOM 2 parsing APIs, as well as the XSLT 1.0 transform API. JAXP offers both a standard API for performing parsing and transformations of XML, and a pluggability API for using various XML parsing engines (such as Xerces and Xalan) to perform the underlying processing. JAXP is covered briefly in Chapter 9. The JAXP APIs are covered in Java in a Nutshell, Fourth Edition, by David Flanagan (O’Reilly & Associates, Inc.).
Naming and Directory Interface) is the Java Enterprise API for
working with networked naming and
services. It allows Java programs to use name servers and directory
servers to look up objects or data by name and search for objects or
data according to a set of specified attribute values. JNDI is
implemented in the
package and its subpackages as a
standard extension to the Java 2 platform.
The JNDI API is not specific to any particular name or directory server protocol. Instead, it is a generic API that is general enough to work with any name or directory server. To support a particular protocol, plug a service provider for that protocol into a JNDI installation. Service providers have been implemented for the most common protocols, such as NIS, LDAP, and Novell’s NDS. Service providers have also been written to interact with the RMI and CORBA object registries. JNDI is covered in detail in Chapter 7.
“message” means different things in
different contexts. In the context of JMS, a message is a chunk of
data that is sent from one system to another in an asynchronous
manner. The data serves as a kind of event notification and is almost
always intended to be read by a computer program, not by a human. In
a nondistributed system that uses the standard Java event model, an
Event object notifies the program that some
important event (such as the user clicking a mouse button) has
occurred. In a distributed system, a message serves a similar
purpose: it notifies some part of the system that an interesting
event has occurred. So you can think of a networked message service
as a distributed event notification system.
JMS is also a good complement to the synchronous communication provided by RMI and CORBA. When an RMI client, for example, makes a remote method call on a server object, the client will block until the remote method returns. JMS provides a way for you to communicate asynchronously with a remote process: you can send your message and carry on with useful work while the message is delivered and processed at the receiving end. If there’s a response from the receiver(s), a callback can be invoked on your end, and you can deal with it then.
Like JNDI and JDBC, JMS is an API layered on top of existing, vendor-specific messaging services. In order to use JMS in your application, you need to obtain a JMS provider implementation that supports your particular message server. Some J2EE application servers bundle their own JMS providers that you can use as message servers; some of them provide easy ways to bridge their application servers to other message services like IBM MQSeries or SonicMQ; others provide neither, and leave it to you to obtain and install a JMS provider.
Chapter 10 provides a short tutorial on using JMS.
Email is another critical communication protocol for enterprise systems. Email is a widespread tool used for interpersonal messaging in a wide variety of contexts, from corporate communications to family-reunion planning, as anyone reading this book is surely aware. In an enterprise application context, email can also be an important tool for end-user event notifications (e.g., a notice that a lower-priced flight has matched your travel profile), for content delivery (e.g., a weekly “e-zine” delivered automatically from a content-management system), and for system monitoring (e.g., a notice to a system administrator that connectivity to a critical information system has been lost).
J2EE’s tool for composing, sending and receiving
email is the JavaMail API, contained in the
package. For dealing with various types
of content in MIME-based email messages, a companion API called the
JavaBeans Activation Framework and provided in the
package is used in conjunction with
JavaMail (the “Activation” in the
name refers to activating a content handler to deal with a particular
type of content). Chapter 11 is a tutorial on the
use of these APIs.
Enterprise JavaBeans do for server-side enterprise programs what JavaBeans do for client-side GUIs. Enterprise JavaBeans (EJB) is a component model for units of business logic and business data. Thin client programming models that take business logic out of the client and put it on a server or in a middle tier have many advantages in enterprise applications. However, the task of writing this middleware has always been complicated by the fact that business logic must be mixed in with code for handling transactions, security, networking, and so on.
The EJB model attempts to separate high-level business logic from low-level housekeeping chores. A bean in the EJB model is an RMI remote object that implements business logic or represents business data. The difference between an enterprise bean and a run-of-the-mill RMI remote object is that EJB components run within an EJB container, which in turn runs within an EJB server. The container and server may provide features such as transaction management, resource pooling, lifecycle management, security, name services, distribution services, and so on. With all these services provided by the container and server, enterprise beans (and enterprise bean programmers) are free to focus purely on business logic. EJB servers are expected to provide a core set of component services, such as lifecycle management, instance pooling, distributed transaction management, and security.
The EJB specification is a document that specifies the contracts to be maintained and conventions to be followed by EJB servers, containers, and beans. Writing EJB components is easy: simply write code to implement your business logic, taking care to follow the rules and conventions imposed by the EJB model.
EJB components can also run within the larger J2EE framework. In addition to the stand-alone EJB component services, EJBs running within a J2EE server can be composed with other components into J2EE applications.
Unlike the other Java Enterprise APIs, EJB is not really an API; it
is a framework for component-based enterprise computing. The key to
understanding Enterprise JavaBeans lies in the interactions among
beans, containers, and the EJB server. These interactions are
described in detail in Chapter 8. There is, of
course, an API associated with the EJB application framework, in the
form of the
You’ll find complete API quick-reference information
for these packages in Part III.
A servlet is a piece of Java code that runs within a server to provide a service to a client. The name “servlet” is a takeoff on applet -- a servlet is a server-side applet. The Java Servlet API provides a generic mechanism for extending the functionality of any kind of server that uses a protocol based on requests and responses.
The Servlet API differs from many other Java Enterprise APIs in that it is not a Java layer on top of an existing network service or protocol. Instead, servlets are a Java-specific enhancement to the world of enterprise computing. With the advent of the Internet and the World Wide Web, many enterprises are interested in taking advantage of web browsers -- universally available thin-clients that can run on any desktop. Under this model, the web server becomes enterprise middleware and is responsible for running applications for clients. Servlets are a perfect fit here. The user makes a request to the web server, the web server invokes the appropriate servlet, and the servlet uses JNDI, JDBC, and other Java Enterprise APIs to fulfill the request, returning the result to the user, usually in the form of HTML-formatted text.
The Servlet API is a standard extension to the Java 2 platform,
implemented in the
javax.servlet.http packages. The
javax.servlet package defines classes that
represent generic client requests and server responses, while the
javax.servlet.http package provides specific
support for the HTTP protocol, including classes for tracking
multiple client requests that are all part of a single client
session. See Chapter 5 for details on servlet
JavaServer Pages (JSPs) are closely related to Java servlets. You can think of JSPs as an alternative approach to creating servlets, in one sense. JSPs are similar to alternative technologies such as PHP and Microsoft Active Server Pages -- they all provide a way to insert dynamic elements directly into HTML pages. In the case of JSPs, dynamic elements invoke Java code through references and calls to JavaBeans, custom tags that act as dynamic macros that are implemented by JavaBeans, or raw Java code snippets. The tie-in with servlets comes when a JSP server receives a request for a JSP. The JSP is converted automatically to a Java servlet, and your Java code snippets and JavaBean references are mapped into the generated servlet. Chapter 6 provides details on writing JSPs.
Both servlets and JSPs can be deployed as “web components” within the J2EE framework, where they depend on all the standard services guaranteed by the servlet and JSP specifications, as well as the ability to reference EJB components, participate in the broader security services of J2EE, etc.
The JTA, or Java Transaction API, is a Java Enterprise API for managing distributed transactions. Distributed transactions are one of the things that make distributed systems more complicated than nondistributed programs. To understand distributed transactions, you must first understand simple, nondistributed transactions.
A transaction is a group of several operations that must behave atomically: as if they constituted a single, indivisible operation. Consider a banking application that allows a user to transfer money from a checking account to a savings account. If the two account balances are stored in a database, the application must perform two database updates to handle a transfer -- it must subtract money from the checking account and add money to the savings account. These two operations must behave atomically. To see why, imagine what would happen if the database server crashed after money had been subtracted from the checking account but before it had been added to the savings account. The customer would lose money!
To make multiple operations atomic, we use transactions. In our banking example, we first begin a transaction, then perform the two database updates. While these updates are in progress, no other threads can see the updated account balances. If both updates complete successfully, we end the transaction by committing it. This makes the updated account balances available to any other clients of the database. On the other hand, if either of the database updates fails, we rollback the transaction, reverting the accounts to their original balances. Other clients are again given access to the database, and they see no changes in the account balances. The JDBC API supports transactions on databases. The database server is required to do some complex work to support transactions, but for the application programmer, the API is easy: simply begin a transaction, perform the desired operations, and then either commit or rollback the transaction.
Distributed transactions are, unfortunately, quite a bit more complex than the simple transactions just described. Imagine, for example, a program that transfers money from an account stored in one database to another account stored in a different database running on a different server. In this case, there are two different servers involved in the transaction, so the process of committing or rolling back the transaction must be externally coordinated. Distributed transactions are performed using a complex procedure known as the two-phase commit protocol (the details of this protocol aren’t important here). What is important is that we could write our account transfer code so that it implements the two-phase commit protocol itself, coordinating the entire distributed transaction with the two database servers. This would be tedious and error-prone, however. In practice, distributed transactions are coordinated by a specialized distributed transaction service.
This brings us, finally, to the JTA. The JTA is a Java API for working with transaction services. It defines a Java binding for the standard XA API for distributed transactions (XA is a standard defined by the Open Group). Using the JTA, we can write a program that communicates with a distributed transaction service and uses that service to coordinate a distributed transaction that involves a transfer of money between database records in two different databases.
Unfortunately, however, using the JTA in this way is still complex
and error-prone. Modern enterprise applications are typically
designed to run within some kind of application server, such as an
Enterprise JavaBeans server or a full J2EE server. The server uses
JTA to handle distributed transactions transparently for the
application. Under this model, JTA becomes a low-level API used by
server implementors, not by typical enterprise programmers.
Therefore, this book doesn’t include a tutorial
chapter on JTA. It does, however, contain a complete API quick
reference for the
javax.transactions.xa packages (see Part III).
Chapter 8 also has a brief section on JTA, since
it is one of the supporting APIs that provides EJB with its