Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Mastering Enterprise JavaBeans™ and the Java 2 Platform, Enterprise Edition - Roman E

..pdf
Скачиваний:
41
Добавлен:
24.05.2014
Размер:
6.28 Mб
Скачать

594 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

JNDI. Vendors of directory services provide service providers to access their particular directory. The service providers know the particular semantics and protocol the directory service uses. JNDI provides a high-level client API that is independent of any particular directory service protocol.

JDBC. Vendors of databases, as well as third parties, provide drivers that know how to access a particular database. The driver knows the access method for interacting with the desired database. For example, Oracle provides database drivers to access its Oracle database. JDBC provides a high-level client API that is independent of any particular database access method.

JDBC is based on the Open Database Connectivity (ODBC) standard. ODBC is a relational database API that is language-independent. JDBC represents the Java language bindings of ODBC. Both Sun and Microsoft provide JDBC-ODBC drivers, which can connect any JDBC data source to an ODBC data source. You can also use JDBC to connect to a database directly (circumventing ODBC drivers) with a native JDBC driver that does not use the JDBC-ODBC bridge. Thus, JDBC is a unifying API for accessing any kind of relational database, whether ODBCbased or not.

If you’re new to JDBC, you can get ramped up by going through the JDBC tutorial. This is a free service provided by Sun Microsystems. See http://java.sun.com/ docs/books/tutorial/jdbc/index.html. The new JDBC 2.0 standard extension documentation is available at http://java.sun.com/products/jdbc/html/.

Although JDBC by itself is great for accessing relational databases, it is hardly a complete API for the enterprise. The problem with JDBC is that you need to hard-code the location of your database (i.e., the machine name and port at which your database resides). You also have to hard-code the data source’s configuration parameters, as well as the particular JDBC driver being used. This impedes maintenance of a multi-tier solution. If you want to switch data sources, there is no automatic way for each of your machines to become aware of the new database. You have to manually administer each machine and change the database driver and parameters.

To alleviate this problem, our example is going to make use of JNDI to store a JDBC data source object in a well-known JNDI tree location.

What Is a JDBC DataSource?

JDBC programmers who have not worked with the new JDBC 2.0 specification, may be unfamiliar with JDBC DataSource objects. A JDBC DataSource is an entity that doles out database connections. You are responsible for asking the

Go back to the first page for a quick link to buy this book online!

Understanding the Java Naming And Directory Interface (JNDI) 595

DataSource for a connection to a database and then releasing that connection when you are done.

The JDBC DataSource is the evolution of the classic JDBC DriverManager. The chief differences between the two are these:

1.JDBC DataSources can automatically pool and reuse database connections for multiple clients. This is absolutely necessary for portable database connection pooling in an Enterprise JavaBeans environment.

2.DataSource objects can be stored in a JNDI tree. Java clients who wish to access a database via JDBC will acquire this DataSource from the wellknown JNDI location. If the database vendor or location changes, we need to change only the single DataSource object stored in the JNDI tree. The new DataSource will automatically be used by clients when they request the DataSource. This is shown in Figure B.9.

Storing Java Objects in Directories

To store our JDBC DataSource, we’ll need to learn how JNDI allows you to store Java objects in directories. Using JNDI, you can instantiate a Java object, serialize the instance into a bitstream (see Appendix A for an explanation of serialization),

2: Lookup DataSource in well-known JNDI tree location

JNDI client code

Initial Context

JDBC

DataSource

1: Store DataSource

in JNDI tree

Figure B.9 Using JNDI to store a JDBC DataSource.

Go back to the first page for a quick link to buy this book online!

596 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

and store the serialized object into a directory structure using JNDI. Later, another piece of code elsewhere on the network can retrieve and deserialize the object from the directory, load the class, and then finally use the object.

There are two ways to store a Java object in a directory using JNDI:

Store your serialized Java object as a whole directory context. When you store a Java object in a JNDI tree, JNDI takes care of the implementation details for you. How is the actual serialized data represented in the JNDI tree? Well, because each JNDI service provider may store data differently, the actual mechanism of storing serialized Java objects is service provider-dependent. If you’re curious, see the book’s accompanying Web site for the LDAP scheme of storing objects.

Store your serialized Java object as a directory context attribute. As we learned previously, a directory context can contain attributes, such as username and password strings. Why not store your Java object as an attribute of a directory context? You can do this if you like.

Under certain circumstances, you may not want to store a whole Java object in a JNDI tree at all. Instead, you can store a compact JNDI reference that knows how to construct the real object. This concept is explained later in this chapter.

Implementing Our JNDI-JDBC Code

This example will use Java serialization to bind a JDBC DataSource to a JNDI tree. Note that this implementation is only illustrative, and it is not in any way prescriptive. You may encounter JDBC vendors that use other mechanisms rather than serialized objects to store their JDBC DataSources in a JNDI tree as well.

TestDataSource.java

To illustrate binding a JDBC DataSource object to a directory structure, we needed to invent our own test pseudo-DataSource, shown in the code below. We’ll simply implement the methods required for a JDBC DataSource and provide no implementation. Note that most of the methods are simply required methods of the parent DataSource interface. The code is shown in Source B.3.

The key point to notice about TestDataSource is that it implements java.io

.Serializable. This means any service provider (such as the LDAP service provider) that knows how to store serialized objects will automatically do so when the service provider encounters our DataSource. When a Java client later tries to retrieve the DataSource, the service provider will automatically deserialize the object.

Go back to the first page for a quick link to buy this book online!

Understanding the Java Naming And Directory Interface (JNDI) 597

package com.wiley.compBooks.roman.jndi.jdbc;

import java.sql.*; import javax.sql.*; import javax.naming.*; import java.io.*;

/**

*Dummy class for a JDBC 2.0 DataSource.

*We implement Serializable so we can be stored in a JNDI tree

*that supports storage of Serialized objects. LDAP is one example. */

public class TestDataSource implements DataSource, Serializable {

private String loc, name; private int port;

private int loginTimeout = 0; private PrintWriter logWriter;

public void setDatabaseServer(String loc) { this.loc = loc;

}

public String getDatabaseServer() { return loc;

}

public void setDatabaseName(String name) { this.name = name;

}

public String getDatabaseName() { return name;

}

public void setDatabasePort(int port) { this.port = port;

}

public int getDatabasePort() { return port;

}

public Connection getConnection() { return null;

}

Source B.3 TestDataSource.java (continues).

Go back to the first page for a quick link to buy this book online!

598 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

public Connection getConnection(String username, String pass) { return null;

}

public void setLoginTimeout(int seconds) { this.loginTimeout = seconds;

}

public int getLoginTimeout() { return 0;

}

public void setLogWriter(PrintWriter out) { logWriter = out;

}

public java.io.PrintWriter getLogWriter() { return logWriter;

}

}

Source B.3 TestDataSource.java (continued).

Client.java

To test our DataSource, we’ve written a small client application. It performs the following tasks:

■■Sets up a DataSource with database-specific parameters

■■Binds the DataSource to a JNDI tree

■■Retrieves the DataSource from the JNDI tree and then uses it

The code is shown in Source B.4.

package com.wiley.compBooks.roman.jndi.jdbc;

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

Source B.4 Client.java (continues).

Go back to the first page for a quick link to buy this book online!

Understanding the Java Naming And Directory Interface (JNDI) 599

/**

* Demonstration client using JDBC via JNDI */

public class Client {

/**

* Binds a JDBC 2.0 DataSource to a JNDI tree, then retrieves it */

public static void main (String[] args) { try {

/*

* Instantiate our fictitious DataSource */

TestDataSource source = new TestDataSource();

/*

* Set up the database machine name */

source.setDatabaseServer("edro.middleware-company.com");

/*

* Set up the database machine port */

source.setDatabasePort(1521);

/*

* Set up the image name of the database */

source.setDatabaseName("MyDB");

/*

*Retrieve a JNDI initial context from the System

*properties.

*/

Context ctx =

new InitialContext(System.getProperties());

/*

*Bind the DataSource to a JNDI tree under the

*node "dn=MyDB"

*/

ctx.rebind("dn=MyDB", source);

/*

* Release the old reference to the data source.

Source B.4 Client.java (continues).

Go back to the first page for a quick link to buy this book online!

600 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

* Retrieve a new reference from the JNDI tree */

source = null;

source = (TestDataSource) ctx.lookup("dn=MyDB");

/*

* Make sure the retrieved object has our set fields */

System.out.println("DataSource returned, bound to " + source.getDatabaseServer() + ":" + source.getDatabasePort() + ":" + source.getDatabaseName());

/*

*Request a database connection. Specify my

*authentication to ensure that I can use it.

*/

Connection con = source.getConnection("login", "pass");

}

catch (Exception e) { e.printStackTrace();

}

}

}

Source B.4 Client.java (continued).

The following is the output after running the client program:

java -Djava.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory

-Djava.naming.provider.url=ldap://louvre:389/o=Airius.com

com.wiley.compBooks.jndi.jdbc.Client

security properties not found. using defaults.

DataSource returned, bound to edro.middleware-company.com:1521:MyDB

The program successfully deserialized the stored information for our client’s use. Now let’s try running it with the File System service provider:

java

-Djava.naming.factory.initial=com.sun.jndi.fscontext.RefFSContextFactory

-D java.naming.provider.url=file:c:\

com.wiley.compBooks.jndi.jdbc.Client

javax.naming.OperationNotSupportedException: Can only bind References or

Referenceable objects

Go back to the first page for a quick link to buy this book online!

Understanding the Java Naming And Directory Interface (JNDI) 601

This exception is expected, because the File System service provider happens not to be able to store serializable objects.

Advanced JNDI: Combining JNDI with EJB

EJB uses the Java Naming and Directory Interface extensively. It is used for naming and locating many goodies in the EJB world. The EJB container vendor can choose to exploit JNDI for storing any kind of information. For example, a value-add of an EJB container might be a mechanism for monitoring a deployment, such as what machines are up, what machines are down, and how many beans are in memory. Using JNDI, this monitoring information can be stored in a directory tree and then displayed on a centralized GUI on a system administrator’s machine. Containers can use JNDI to store resources used by the container and many other kinds of information—the possibilities are endless.

JNDI and EJB Home Objects

The greatest use of JNDI—at least for EJB clients—is to acquire a reference to a home object. If you’ll recall from Chapter 3, a home object is a factory that creates beans, or a locator that finds beans somewhere in a database.

In the real world, a home object is simply an RMI remote object because it implements java.rmi.Remote (described in Appendix A). Remote objects are networked objects whose methods can be called from clients residing on remote hosts.

When you write EJB client code, you use the JNDI API to locate home objects over the network. Home objects are bound to a well-known directory location so that clients know where to look for them.

As an example, here is a segment of the “Hello, World!” example shown in Chapter 4:

/*

* Get System properties for JNDI initialization */

Properties props = System.getProperties();

/*

*Get a reference to the HelloHome Object - the

*factory for Hello EJB Objects

*/

Context ctx = new InitialContext(props);

HelloHome home = (HelloHome) ctx.lookup("HelloHome");

Go back to the first page for a quick link to buy this book online!

602 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

As you can see, to acquire a reference to a home object using EJB, you use the basic JNDI steps we’ve learned in this chapter, including the following:

■■Declaratively specifying environment properties using properties files or System properties. These properties detail the JNDI service provider used in your deployment, the initial context URL, the security settings you need to access a directory tree, and more.

■■Creating an initial context factory using those environment properties.

■■Using JNDI to look up an object stored in a directory tree.

When you perform a JNDI lookup() for a home object, a typical EJB container might use JNDI to return a Java RMI remote stub. The remote stub is a proxy for the actual home object, which is located elsewhere (perhaps on the EJB Server machine). Once the client has the stub, he or she can invoke remote methods on the home object through the remote stub proxy. And remember, because Java RMI remote stubs are in themselves serializable objects and can be passed over the network, they can be stored in a JNDI tree.

The EJB container could also use referenceable objects and object factories to store objects, which the curious reader can learn about in the following sidebar. There is yet a third mechanism: The EJB container could use the RMI registry JNDI service provider as an assistance to locating remote objects. The RMI registry service provider is very useful in many scenarios, which we’ll learn about shortly.

Other Uses of JNDI

The new EJB 1.1 specification has extended the use of JNDI to several other technologies as well, including the following:

■■Using JNDI to acquire a reference to the Java Transaction API (JTA)

UserTransaction interface

■■Using JNDI to connect to resource factories, such as JDBC drivers or Java Message Service (JMS) drivers

■■Using JNDI for beans to look up other beans

See Appendix D for more information about the new changes in EJB 1.1.

Advanced JNDI: Combining JNDI with Java RMI

As you learned in Appendix A, finding networked objects using Java RMI is not a pretty picture. When you make a remote object available to be invoked on by remote hosts, you register that object with an RMI registry. When a client wants

Go back to the first page for a quick link to buy this book online!

Understanding the Java Naming And Directory Interface (JNDI) 603

JNDI References and Object Factories

As we showed in the JDBC example, you can use JNDI to store a Java object in a directory structure by serializing that object, thus storing a copy of the object’s original state. But sometimes this is infeasible, perhaps because the Java object is particularly large. Or perhaps the Java object is a database connection or network connection and has nonserializable state. Some types of directories do not even support storage of Java objects. Thus, an alternative mechanism for storing Java objects is needed.

This is where JNDI references come into play. JNDI references are like handles to objects. When you store a reference into a directory, you’re effectively storing a handle to an object, rather than the whole object itself. The JNDI reference that you store is a compact representation that provides enough information to construct the real object. Note that while JNDI references are powerful, every JNDI service provider does not support them.

Storing Java objects in a JNDI tree via serialization versus using JNDI references has many parallels in Java RMI. When you program with Java RMI, you can pass parameters to remote methods in one of two ways: by serializing the object (pass by value) or by giving the client a remote stub, or proxy, for the actual Java object (pass by reference). Storing a serialized Java object in a directory via JNDI is similar to RMI parameter serialization, while storing JNDI references is similar to passing remote stubs via

Java RMI.

Complementing JNDI references are object factories. Object factories are pieces of Java code that know how to reconstruct Java objects from a JNDI reference. For example, an object factory for printers might know how to establish a printer connection based on printer location information stored in a JNDI reference retrieved from a directory structure. You are empowered as a JNDI programmer to specify exactly how the object factory reconstructs objects because you can write your own object factories.

We briefly describe the classes and interfaces related to JNDI references and object factories in Table B.3. If you’re interested in learning more, see the JNDI Tutorial on the Sun Microsystems home page link on the book’s Web site, where the topic is covered in full.

Table B.3 JNDI Interfaces and Classes Used for Storing Referenceable Objects

CLASS OR INTERFACE

DESCRIPTION

Interface javax.naming.Referenceable

A class should implement this interface if you want

 

JNDI to store a JNDI Reference, rather than a serial-

 

ized object, when instances of the class are bound

 

to a directory. You should implement the getReference

 

method that generates a Reference to yourself—this

 

is called when you are about to be stored.

 

 

 

continued

Go back to the first page for a quick link to buy this book online!