JNDI as a component registry (EJB 3)

JNDI is the JDBC of naming and directory services. Just as JDBC provides a standard Java EE API to access all kinds of databases, JNDI standardizes naming and directory service access. If you’ve ever used a Lightweight Directory Access Protocol (LDAP) such as a Microsoft Active Directory server, you already know what a naming and directory service is.

In simple terms, a naming service provides the ability to locate a component or service by name. You give a naming service the complete name for a resource and it figures out how to get you a handle to the resource that you can use. Domain Name Service (DNS) is a relatively well-known example of a naming service. When we point our web browser to http://yahoo.com, the DNS server conducts a lookup and directs us to the right IP address for Yahoo. The RMI registry is another example of a naming service. In a sense, even an operating system file manager is a naming service. You give the file manager the complete path to a file and it gives you a handle to the file you are looking for.

As figure A.2 shows, JNDI provides a uniform abstraction over a number of different naming services such as LDAP, DNS, Network Information Service (NIS), Novell Directory Services (NDS), RMI, Common Object Request Broker Architecture (CORBA), and so on. Once you have an instance of a JNDI context, you can use it to locate resources in any underlying naming service available to the context. Under the hood, JNDI negotiates with each available naming service given the name of a resource to figure out where to look up the service’s actual location.


JNDI provides a single unified API to access various naming services such as LDAP, NDS, NDS, NIS, RMI, and CORBA. Any naming service with a JNDI Service Provider Interface (SPI) provider can be plugged into the API seamlessly.

Figure A.2 JNDI provides a single unified API to access various naming services such as LDAP, NDS, NDS, NIS, RMI, and CORBA. Any naming service with a JNDI Service Provider Interface (SPI) provider can be plugged into the API seamlessly.

An example JNDI tree for an application server. All global resources such as jdbc and jms are bound to the root context of JNDI tree. Each application has its own application context, and EJBs and other resources in the application are bound under the application context.

Figure A.3 An example JNDI tree for an application server. All global resources such as jdbc and jms are bound to the root context of JNDI tree. Each application has its own application context, and EJBs and other resources in the application are bound under the application context.

Like RMI, JNDI plays a vital role in EJB 3, although it is by and large hidden behind the scenes (also like RMI, JNDI used to be a lot more visible and made EJB much more cumbersome as of EJB 2). In a very real sense, JNDI is to EJB what the RMI registry is to RMI. JNDI is used as the central repository for resources managed by the container.

As a result, every bean managed by the container is automatically registered with JNDI. In addition, a typical container JNDI registry will store JDBC data sources, JMS queues, JMS connection factories, JPA entity managers, JPA entity manager factories, and so on. Whenever a client (such as an EJB) needs to use a managed resource, they use JNDI to look up the resource by its unique name. Figure A.3 shows how a typical JNDI tree for a Java EE application server might look.

As you can see in figure A.3, resources are stored in a JNDI tree in a hierarchical manner. This means that JNDI resource names look much like Unix file pathnames (they also sometimes start with a protocol specification such as java:, much like a URL address you would enter in a browser navigation bar). As with RMI, once you procure a handle to a resource from a JNDI context, you can use it as though it were a local resource.

To use a resource stored in the JNDI context, a client has to initialize the context and look up the resource. Despite the robustness of the JNDI mechanism itself, the code to do so isn’t that intimidating. The code in listing A.1 looks up a JDBC data source from JNDI and creates a new connection from it. As you might imagine, the JDBC connection then might be used to issue SQL to the underlying database pointed to by the retrieved data source.

Listing A.1 Looking up a JDBC data source using JNDI

Listing A.1 Looking up a JDBC data source using JNDI

In listing A.1, the JNDI lookup takes place in the first two lines. First, an Initial-Context object is instantiated. The InitialContext object connects to any given JNDI tree. In the case of the parameter-less version of the constructor used in listing A.1, the InitialContext object connects to the "default" JNDI tree. The JNDI defaults are determined by the contents of a file named jndi.properties that can be stored anywhere in the JVM’s CLASSPATH. The Java EE application server usually provides this properties file, and the settings in the file typically point to the JNDI tree of the local application server. As a result, the default InitialContext constructor is most useful while looking up resources within the same JVM. If you are looking up a resource (such as an EJB) on a remote application server, then you must feed environment properties to the InitialContext constructor. This can be done as follows:

tmp4B-45_thumb

In the example, the custom Properties entries specify that we are trying to connect to a remote Oracle application server JNDI tree. Note JNDI connection properties are vendor (application server) specific and our example cannot be used universally, so you should consult with your application server’s documentation to see how you can connect to it remotely. In general, you might find that most application servers require a common set of JNDI properties defined as constants in the Context interface. Table A.1 summarizes the most common environment properties that are used for Java EE application servers.

Note that instead of providing environment properties programmatically, you can also simply modify the jndi.properties file in your runtime CLASSPATH. If you are using EJB 3 DI, this is the only way of connecting to a remote server.

Table A.1 Common JNDI environment properties required for creating an initial context to connect to a remote JNDI service provider in a Java EE environment. These are specified either as system properties in the jndi.properties file in the JVM at the client side or as Property object entries passed to the constructor in your Java code. Of these options, a properties file is recommended as it improves maintainability of your application code.

Property Name

Description

Example Value

tmp4B-46

The name of the factory class that will be used to create the context

oracle.j2ee.rmi.

RMIInitialContextFactory

tmp4B-47

The URL for the JNDI service provider

ormi://localhost:23791/ chapter1

tmp4B-48

The username or identity for authenticating the caller in the JNDI service provider

oc4jadmin

tmp4B-49

The password for the username/principal being used for authentication

welcome!

In the second line of listing A.1, the lookup is performed using the context instantiated in the first line. The single parameter of the lookup method is the full name of the resource you are seeking. In our case, the JNDI name of the JDBC data source we are looking for happens to be jdbc/ActionBazaarDS. Note that because the lookup method returns the Object type, we must cast the retrieved resource to the correct type. In the case of EJBs, references returned by JNDI must be cast to a valid business interface implemented by the EJB.

While the code in listing A.1 looks harmless, don’t be taken in by appearances. JNDI lookups were one of the primary causes for EJB 2′s complexity. First of all, you had to do lookups to access any resource managed by the container, even if you were only accessing data sources and EJBs from other EJBs located in the same JVM. Given that most EJBs in an application depend on other EJBs and resources, imagine the lines of repetitive JNDI lookup code littered across an average business application! To make matters worse, JNDI names of resources aren’t always that obvious to figure out, especially for resources that are bound to the environment naming context (which must use the arcane java:comp/env/ prefix for portability of applications instead of using a global JNDI name).

The good news is that except for certain corner cases, you won’t have to deal with the evils of JNDI in EJB 3. EJB 3 puts the mechanical details of JNDI lookups well hidden behind metadata-based DI. DI does such a great job in abstraction that you won’t even know that JNDI lookups are happening behind the scenes, even for remote lookups.

Next post:

Previous post: