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: Working with Databases

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.

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.

RMI: Remote Method Invocation

Remote method 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 java.rmi package 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 object 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 Java Native 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 CORBA-based 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.

The java.rmi package makes it easy to create networked, object-oriented programs. 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.

Java IDL: CORBA Distributed Objects

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.

CORBA (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 the 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 objects.

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.

JAXP: XML Parsing and Messaging

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.).

JNDI: Accessing Naming and Directory Services

JNDI (Java Naming and Directory Interface) is the Java Enterprise API for working with networked naming and directory 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 javax.naming 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.

JMS: Enterprise Messaging

JMS (Java Message Service) is the Java Enterprise API for working with networked messaging services and for writing message-oriented middleware (fondly referred to as MOM).

The word “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.

JavaMail: Email-Based Messaging

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 javax.mail 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 javax.activation 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: Distributed Components

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 javax.ejb and javax.ejb.deployment packages. You’ll find complete API quick-reference information for these packages in Part III.

Servlets and JavaServer Pages (JSPs): Web-Based Components/UIs

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.

For the most part, servlets are used behind web servers for dynamic generation of HTML content. On the growing number of web/application servers that support them, servlets are a Java-based replacement for CGI scripts. They can also replace competing technologies, such as Microsoft’s Active Server Pages (ASP) or Netscape’s Server-Side JavaScript. The advantage of servlets over these other technologies is that servlets are portable among operating systems and among servers. Servlets are also compiled objects that are persistent between invocations, which gives them major performance benefits over parsed CGI programs. Servlets also have full access to the rest of the Java platform, so features such as database access are automatically supported.

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 and 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 programming.

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.

JTA: Managing Distributed Transactions

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.transaction and 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 distributed transaction support.

Get Java Enterprise in a Nutshell, Second Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.