HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

Enterprise system developers have embraced the Java 2 Platform, Enterprise Edition (J2EE) development model and the collection of Java APIs for developing standard, component-based, multitiered enterprise solutions. These APIs provide a standard mechanism for accessing pertinent system services typically required for enterprise systems development such as databases (JDBC), asynchronous communication (JMS), transaction support (JTA), e-mail (JavaMail/JAF), distributed computing (RMI/IIOP), naming and directory services (JNDI) and Web client presentation (servlets/JSP). Enterprise JavaBeans (EJB) is a core J2EE API that provides for a standard server-side component model.

JNDI is an enterprise Java API that provides naming and directory functionality using a common set of classes and interfaces that are independent of a specific naming and directory service implementation. A standard extension to the Java platform, some developers consider its use to be limited to interfacing with directory services such as LDAP. However, this narrow view fails to fully appreciate its flexibility and power. JNDI is a particularly important J2EE API given its pervasive use among other important enterprise Java APIs such as EJB, JDBC and JMS. This article introduces JNDI as a common framework for locating enterprise computing components using enterprise Java APIs such as EJB and the Java Message Service (JMS).

Naming and Directory Services
Before discussing JNDI, a quick overview of some naming and directory service terminology might be helpful. Although there are a number of existing naming and directory services, they all share some common concepts and terms that are helpful in understanding the flexibility of JNDI. Here's a list of some well-known naming and directory services:

  • LDAP (Lightweight Directory Access Protocol): A directory services protocol that enables clients to manage and query a hierarchical repository of entries and attributes. For example, LDAP can be used to locate information pertaining to users (e.g., X.509 certificates) and network resources (e.g., available printers).

  • NIS (Network Information System): A naming service, formerly known as yp, developed by Sun. It allows users to access systems on other hosts with a single user ID and password.
  • COS Naming Service: The CORBA naming service that enables CORBA clients to locate CORBA server objects using names.
  • DNS (Domain Name Service): A distributed naming service that allows users to refer to hosts using easily remembered names (e.g., anthony) instead of IP addresses.

A naming system can be described as a mechanism that associates names with objects and provides a way to find an object based on a given name. A binding can be described as an association of a name with an object. A file name, for example, is associated with a reference that an application can use to access a file. A binding is defined relative to a context. A context comprises a set of bindings. For example, /home, the UNIX file directory, is a context comprising a set of bindings pertaining to files and directories. The process of looking up an object in a context based on a name is known as resolution. This would be loosely analogous to finding a phone number of a person by looking up his or her name.

All objects in a context are named using a specific naming convention. Thus a naming system can be described as a set of contexts with the same naming convention. For example, in file systems a name in the DOS file system (c:\boot.ini) has a different naming syntax than a name in the UNIX file system (/etc/gated.conf). In contrast, LDAP's naming syntax reads names from right to left. An example of an LDAP name is "cn=Ian, o=glenayre, c=us".

When discussing naming systems, we need to describe different types of names. An atomic name is simply an indivisible component of a name. For example, in the directory name /etc/mail, mail is an atomic name. A compound name is a sequence of atomic names that conforms to a specific naming convention. For example, /var/adm is a compound name. Composite names span multiple naming systems. For example, a URL such as http://www.glenayre.com/index.html spans different naming systems such as DNS (Domain Name Service), and the UNIX file system. Namespace is a term used to describe the collection of names in a naming system.

A directory service is an extension of the naming service. A directory is typically used to associate attributes with objects. For example, a user object could have attributes such as the user's name, phone number, e-mail address and fax number. Directory services provide functionality for evaluating and modifying attributes attached to contexts and the ability to search a context using those attributes as a filter.

Although naming and directory services such as LDAP, NIS and DNS vary in their exact implementation from one service to the next, JNDI provides a common framework for accessing existing heterogeneous naming and directory services in a way that hides their protocol and implementation details.

JNDI Architecture
The JNDI architecture can be described in terms of three main layers. JNDI's layered architecture enables clients to use a common API to work with different naming and directory services (DNS, LDAP, COS Naming).

The top layer is the application layer that uses the JNDI API. The middle layer is the JNDI API that defines a set of classes and interfaces for supporting common naming and directory service functionality. This is the layer that frees a developer from having to deal with specific naming and directory service complexities. It relies on concrete implementations of naming and directory services.

The bottom layer is the implementation layer. Since the JNDI API is independent of specific naming and directory services, it's up to service providers to provide implementations using the JNDI Service Provider Interface (SPI), which can access different naming and directory services. These service provider implementations can easily be plugged in to the JNDI architecture, thus enabling the application layer to access their services using the JNDI API. Figure 1 shows an overview of the JNDI architecture.

Figure 1
Figure 1:

The JNDI API comprises a number of packages. To provide a more concrete description of the JNDI architecture, the main JNDI packages are summarized below.

  • javax.naming package: Defines classes (e.g., Binding) and interfaces (e.g., Name) for accessing naming services
  • javax.naming.directory package: Defines classes (e.g., InitialDirContext) and interfaces (e.g., Attribute) for accessing directory services
  • javax.naming.event package: Defines classes (e.g., NamingEvent) and interfaces (e.g., NamingListener) for event notification when accessing naming and directory services
  • javax.naming.ldap package: Defines classes (e.g., InitialLdapContext) and interfaces (e.g., ExtendedRequest) to provide support for LDAPv3 extended operations and controls
  • javax.naming.spi package: Defines classes (e.g., NamingManager) and interfaces (e.g., InitialContextFactory) used to access existing naming and directory services using JNDI's SPI
Now that you have a basic overview of JNDI's architecture, we can explore some main JNDI classes and interfaces needed to support a JNDI client. Table 1 describes some fundamental JNDI classes and interfaces.

Table 1

Note that if you're using the JNDI API within a multithreaded application context, you may need to develop your own locking mechanisms because instances of some JNDI classes you may use (e.g., Binding) don't support concurrent access.

Explore Your File System Using JNDI
To introduce you to some of the JNDI classes and interfaces, I'll use a simple and familiar use case of getting a listing of a directory in a local file system.

The first task is to obtain a reference to an InitialContext so as to provide an entry point to a naming system. Before accessing naming and directory services, JNDI client applications can specify environment properties (e.g., security preferences, context factory class name) of a context. A context's environment is represented as a hashtable that's used in the constructor of InitialContext. Note the use of a file system provider's factory class name for creating the initial context and the use of a URL to specify the file system URL (e.g., file:///). This type of environment specification detail could be encapsulated in a factory method or facade depending on your application's design.

Hashtable contextEnv = new Hashtable();
contextEnv.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
contextEnv.put(Context.PROVIDER_URL, directoryURL);
Context context = new InitialContext(contextEnv);
After obtaining a reference to a context, we can get a list of bindings from the context. From the binding we can obtain a name of an entry in the file system's directory.
try {
NamingEnumeration bindingList = context.listBindings("");
while (bindingList.hasMore()) {
Binding binding = (Binding) bindingList.next();
System.out.println(binding.getName());
}
} catch (NamingException ex) {
ex.printStackTrace();
}

Although security properties (e.g., identity of principal, security level) can be specified in the environment properties prior to connecting to a naming or directory service, JNDI doesn't specify a specific security model for accessing naming and directory servers. Security-related operations are the responsibility of individual service providers. However, JNDI can be used to query a resource (e.g., LDAP server) to determine the security mechanisms (e.g., Simple Authentication and Security Layer [SASL] mechanisms) supported. In addition, JNDI does define security-related exceptions such as javax.naming.AuthenticationException, which are thrown when a user can't be authenticated by a naming or directory service.

Use of JNDI in EJB
Now that you've seen how some of the main JNDI classes and interfaces are used, let's discuss the role of JNDI in the EJB specification. EJB is a standard component architecture for developing distributed applications using Java. A naming service is an important distributed computing system service that enables a client to obtain a reference to a distributed object using a name. For those who have developed systems using CORBA, you've probably used the CORBA naming service or an ORB vendor-specific mechanism to obtain a reference to a CORBA object.

For EJB systems JNDI is used to obtain references to an EJB's home interface, a factory interface for creating, finding and removing EJBs. In addition, JNDI can be used to gain access to an EJB's environmental entries, which are used to customize an EJB's behavior. I'll discuss these two uses of JNDI in the EJB 1.1 specification.

Locating EJB Home Interfaces
An EJB client can locate an EJB's home interface through the standard JNDI API. The EJB container is responsible for making the home interfaces of its deployed beans available to an EJB client using JNDI. A client's JNDI namespace may be configured to include the home interfaces of EJBs that are installed in containers on other hosts.

Although security properties (e.g., identity of principal, security level) can be specified in the environment properties prior to connecting to a naming or directory service, JNDI doesn't specify a specific security model for accessing naming and directory servers. Security-related operations are the responsibility of individual service providers. However, JNDI can be used to query a resource (e.g., LDAP server) to determine the security mechanisms (e.g., Simple Authentication and Security Layer [SASL] mechanisms) supported. In addition, JNDI does define security-related exceptions such as javax.naming.AuthenticationException, which are thrown when a user can't be authenticated by a naming or directory service.

Use of JNDI in EJB
Now that you've seen how some of the main JNDI classes and interfaces are used, let's discuss the role of JNDI in the EJB specification. EJB is a standard component architecture for developing distributed applications using Java. A naming service is an important distributed computing system service that enables a client to obtain a reference to a distributed object using a name. For those who have developed systems using CORBA, you've probably used the CORBA naming service or an ORB vendor-specific mechanism to obtain a reference to a CORBA object.

For EJB systems JNDI is used to obtain references to an EJB's home interface, a factory interface for creating, finding and removing EJBs. In addition, JNDI can be used to gain access to an EJB's environmental entries, which are used to customize an EJB's behavior. I'll discuss these two uses of JNDI in the EJB 1.1 specification.

Locating EJB Home Interfaces
An EJB client can locate an EJB's home interface through the standard JNDI API. The EJB container is responsible for making the home interfaces of its deployed beans available to an EJB client using JNDI. A client's JNDI namespace may be configured to include the home interfaces of EJBs that are installed in containers on other hosts.

Consider the use case of an application that provides messaging and informational services (e.g., messages, stock quotes, news). To support this client, assume we have a coarse-grained distributed facade in the form of a ContentMessageBroker session bean. To obtain messages and content (e.g., stock quotes), we need to obtain a reference to the ContentMessageBroker's home interface. Following is a code snippet to show how a messaging and informational services client obtains a reference to the ContentMessageBroker's home object.

try {
InitialContext = new InitialContext();
Object obj = initialContext.lookup("ContentMessageBrokerBean");
brokerHome = (ContentMessageBrokerHome) PortableRemoteObject.narrow(obj,
arch40.proto.ContentMessageBrokerHome.class);
} catch (Exception nameException) {
nameException.printStackTrace();
}
A useful development practice is to wrap the code shown above in a Facade class. This client-side facade can shield your application's business layer from specific distributed computing configuration and remote object access details (e.g., to obtain a reference to a session bean). For more information on the Facade design pattern consult Gamma et al.'s Design Patterns (1995).

As we discuss the use of JNDI in distributed systems, it's appropriate to discuss load balancing, a requirement of most large-scale enterprise systems. A number of load-balancing strategies are available, including IP-level load balancing and DNS "round robin"; for CORBA systems the naming service can be used to provide a load-balancing solution. For EJB systems JNDI has been used to provide a proprietary load-balancing solution for an EJB Server in the form of a JNDI service provider that uses an underlying naming service. Thus this service provider implementation is used to select an object from a cluster of servers using JNDI interfaces.

Accessing EJB Environment Context
An attractive feature of component-based development is the ability to customize a component's behavior without having to change its source code. An EJB can be customized using environmental entries specified in the EJB deployment descriptor. The EJB container provides an implementation of a JNDI context that comprises the EJB's environment and makes it available to an instance of that EJB at runtime using the JNDI. This use of JNDI is another way for an EJB to interface with its container. Further, an EJB container must be able to save and restore a reference to this JNDI context across EJB instance passivation.

Suppose we consider a contrived example of a session bean that maintains a configurable number of connections to legacy system servers. Let's assume we need to be able to configure the maximum number of connections during deployment. First, the EJB finds the environment naming context using JNDI by creating an InitialContext object. The EJB then looks up the environment naming context using the name java:comp/env. The maxConnections environment entry is declared in the env-entry element in the bean's deployment descriptor - in the EJB 1.1 specification, deployment descriptors are specified using XML. The following code snippet shows how a session bean instance can obtain the maximum number of connections defined during deployment.

// Obtain the EJB's environment naming context
Context initContext = new InitialContext();
Context legacyServerContext = (Context)initContext.lookup("java:comp/env");
// Obtain the maximum number of server connections configured by the Deployer.
Integer maxConnects = (Integer) legacyServerContext.lookup("maxConnections");

Use of JNDI in JMS
JMS is a J2EE API that provides support for interfacing with enterprise messaging systems using two messaging models: point-to-point and publish/subscribe. The intrinsic value of JMS, JavaMail, JNDI and other enterprise Java APIs is that they provide a common interface to existing and disparate enterprise system APIs (mail systems, naming and directory systems, and so on).

JMS defines a specific type of object called administered objects that constitutes JMS configuration information. These objects, such as ConnectionFactory, are created by a JMS administrator and registered so they can be globally accessed by JMS clients using JNDI. ConnectionFactory encapsulates a set of configuration parameters that have been defined by an administrator and is used by a JMS client to create a connection with a particular provider.

JNDI is the convention used by JMS clients to look up JMS administered objects. If an administrator has created and configured a TopicConnectionFactory, for instance, it can be found as follows using JNDI:

Context umsMessages = new InitialContext();
TopicConnectionFactory connectionFactory =
(TopicConnectionFactory) ctx.lookup("javax.jms.TopicConnectionFactory");
The ConnectionFactory can then be used to create a Connection object that is used to interact with an existing messaging system. The use of JNDI in JMS is similar to its use in JDBC - JNDI is used to retrieve references to DataSource objects in order to obtain a database connection. For an introduction to the Java Message Service, visit the JMS Web site listed under "Resources" at the end of this article.

Conclusion
The JNDI API is a powerful API used by other enterprise Java APIs. For those interested in exploring JNDI, version 1.2 is currently available from the JNDI Web site listed below. The JNDI 1.2 class libraries and the COS naming service provider are packaged as part of the Java 2 SDK, Enterprise Edition.

This article did not focus on a core value of JNDI: its ability to access heterogeneous directory services using a common interface. The JNDI Web site lists available service providers for directory services. On a related note, if you're developing systems that use disparate directory services, you might also be interested in exploring Directory Services Markup Language (DSML), an XML language. DSML is an emerging effort to represent directory information using a format that's independent of a specific directory service. A preview release of a JNDI service provider is available from the JNDI Web site.

Resources

  1. Java Naming and Directory Interface: http://www.javasoft.com/products/jndi
  2. Enterprise JavaBeans: http://www.javasoft.com/products/ejb
  3. Java Message Service: http://www.javasoft.com/products/jms
  4. Gamma, E., Helm, R., Johnson, R., and Vlissides, J. (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley.
  5. Directory Services Markup Language: http://www.dsml.org

Author Bio
Ian Moraes, Ph.D., is a principal engineer at Glenayre Electronics where he works on the architecture and design of a unified messaging system. Ian has developed client/server systems for both the financial services and telecommunications industries. Ian can be contacted at: [email protected]

 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.